if/else
if
-else
分支判断和其他语言类似。不同的是,Rust 语言中的布尔判断条件不必使用小括号包裹,且每个条件后面都跟着一个代码块。if
-else
条件选择是一个表达式,并且所有分支都必须返回相同的类型。
Rust
fn main() {
let n = 5;
if n < 0 {
print!("{} is negative", n);
} else if n > 0 {
print!("{} is positive", n);
} else {
print!("{} is zero", n);
}
let big_n =
if n < 10 && n > -10 {
println!(", and is a small number, increase ten-fold");
// 这个表达式返回一个 `i32` 类型。
10 * n
} else {
println!(", and is a big number, half the number");
// 这个表达式也必须返回一个 `i32` 类型。
n / 2
// 试一试 ^ 试着加上一个分号来结束这条表达式。
};
// ^ 不要忘记在这里加上一个分号!所有的 `let` 绑定都需要它。
println!("{} -> {}", n, big_n);
}
输出:
csharp
5 is positive, and is a small number, increase ten-fold
5 -> 50
loop 循环
Rust 提供了 loop
关键字来表示一个无限循环。
可以使用 break
语句在任何时候退出一个循环,还可以使用 continue
跳过循环体的剩余部分并开始下一轮循环。
Rust
fn main() {
let mut count = 0u32;
println!("Let's count until infinity!");
// 无限循环
loop {
count += 1;
if count == 3 {
println!("three");
continue; // 跳过这次迭代的剩下内容
}
println!("{}", count);
if count == 5 {
println!("OK, that's enough");
break; // 退出循环
}
}
}
嵌套循环和标签
Rust
#![allow(unreachable_code)]
#![allow (unused)]
fn main() {
'outer: loop {
println!("Entered the outer loop");
'inner: loop {
println!("Entered the inner loop");
// 这只是中断内部的循环
//break;
// 这会中断外层循环
break 'outer;
}
println!("This point will never be reached");
}
println!("Exited the outer loop");
}
输出:
Entered the outer loop
Entered the inner loop
Exited the outer loop
从 loop 循环中返回
loop
有个用途是尝试一个操作直到成功为止。若操作返回一个值,则可能需要将其传递给代码的其余部分:将该值放在 break
之后,它就会被 loop
表达式返回。
Rust
fn main() {
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2;
}
};
print!("{}", result);
assert_eq!(result, 20);
}
while 循环
while
关键字可以用作当型循环(当条件满足时循环)。
Rust
fn main() {
// 计数器变量
let mut n = 1;
// 当 `n` 小于 101 时循环
while n < 101 {
if n % 15 == 0 {
println!("fizzbuzz");
} else if n % 3 == 0 {
println!("fizz");
} else if n % 5 == 0 {
println!("buzz");
} else {
println!("{}", n);
}
// 计数器值加 1
n += 1;
}
}
for 循环
for 与区间
for in
结构可以遍历一个 Iterator
(迭代器)。创建迭代器的一个最简单的方法是使用区间标记 a..b
。这会生成从 a
(包含此值) 到 b
(不含此值)的,步长为 1 的一系列值。
让我们使用 for
代替 while
来写 FizzBuzz 程序。
Rust
fn main() {
// `n` 将在每次迭代中分别取 1, 2, ..., 100
for n in 1..101 {
if n % 15 == 0 {
println!("fizzbuzz");
} else if n % 3 == 0 {
println!("fizz");
} else if n % 5 == 0 {
println!("buzz");
} else {
println!("{}", n);
}
}
}
或者,可以使用a..=b
表示两端都包含在内的范围。上面的代码可以写成:
Rust
fn main() {
// `n` 将在每次迭代中分别取 1, 2, ..., 100
for n in 1..=100 {
if n % 15 == 0 {
println!("fizzbuzz");
} else if n % 3 == 0 {
println!("fizz");
} else if n % 5 == 0 {
println!("buzz");
} else {
println!("{}", n);
}
}
}
for 与迭代器
for in
结构能以几种方式与 Iterator
互动。如果没有特别指定,for
循环会对给出的集合应用 into_iter
函数,把它转换成一个迭代器。这并不是把集合变成迭代器的唯一方法,其他的方法有 iter
和iter_mut
函数。
这三个函数会以不同的方式返回集合中的数据。
iter
- 在每次迭代中借用集合中的一个元素。这样集合本身不会被改变,循环之后仍可以使用。
Rust
fn main() {
let names = vec!["Bob", "Frank", "Ferris"];
for name in names.iter() {
match name {
&"Ferris" => println!("There is a rustacean among us!"),
_ => println!("Hello {}", name),
}
}
}
输出:
Hello Bob
Hello Frank
There is a rustacean among us!
into_iter
- 会消耗集合。在每次迭代中,集合中的数据本身会被提供。一旦集合被消耗了,之后就无法再使用了,因为它已经在循环中被 "移除"(move)了。
Rust
fn main() {
let names = vec!["Bob", "Frank", "Ferris"];
for name in names.into_iter() {
match name {
"Ferris" => println!("There is a rustacean among us!"),
_ => println!("Hello {}", name),
}
}
}
输出:
Hello Bob
Hello Frank
There is a rustacean among us!
iter_mut
- 可变地(mutably)借用集合中的每个元素,从而允许集合被就地修改。
Rust
fn main() {
let mut names = vec!["Bob", "Frank", "Ferris"];
for name in names.iter_mut() {
*name = match name {
&mut "Ferris" => "There is a rustacean among us!",
_ => "Hello",
}
}
println!("names: {:?}", names);
}
输出:
names: ["Hello", "Hello", "There is a rustacean among us!"]
match 匹配
Rust 通过 match
关键字来提供模式匹配,和 C 语言的 switch
用法类似。
Rust
fn main() {
let number = 1;
// 试一试 ^ 将不同的值赋给 `number`
println!("Tell me about {}", number);
match number {
// 匹配单个值
1 => println!("One!"),
// 匹配多个值
2 | 3 | 5 | 7 | 11 => println!("This is a prime"),
// 试一试 ^ 将 13 添加到质数列表中
// 匹配一个闭区间范围
13..=19 => println!("A teen"),
// 处理其他情况
_ => println!("Ain't special"),
// 试一试 ^ 注释掉这个总括性的分支
}
let boolean = true;
// match 也是一个表达式
let binary = match boolean {
// match 分支必须覆盖所有可能的值
false => 0,
true => 1,
// 试一试 ^ 将其中一条分支注释掉
};
println!("{} -> {}", boolean, binary);
}
输出:
Tell me about 1
One!
true -> 1
解构元组
元组可以在 match
中解构,如下所示:
Rust
fn main() {
let triple = (1, -2, 3);
// 试一试 ^ 将不同的值赋给 `triple`
println!("Tell me about {:?}", triple);
// match 可以解构一个元组
match triple {
// 解构出第二个和第三个元素
(0, y, z) => println!("First is `0`, `y` is {:?}, and `z` is {:?}", y, z),
(1, ..) => println!("First is `1` and the rest doesn't matter"),
// `..` 可用来忽略元组的其余部分
_ => println!("It doesn't matter what they are"),
// `_` 表示不将值绑定到变量
}
}
输出:
Tell me about (1, -2, 3)
First is `1` and the rest doesn't matter
结构枚举
Rust
#[allow(dead_code)]
enum Color {
Red,
Blue,
Green,
RGB(u32, u32, u32),
HSV(u32, u32, u32),
HSL(u32, u32, u32),
CMY(u32, u32, u32),
CMYK(u32, u32, u32, u32),
}
fn main() {
// let color = Color::RGB(122, 17, 40);
let color = Color::Red;
// 试一试 ^ 将不同的值赋给 `color`
println!("What color is it?");
// 可以使用 `match` 来解构 `enum`。
match color {
Color::Red => println!("The color is Red!"),
Color::Blue => println!("The color is Blue!"),
Color::Green => println!("The color is Green!"),
Color::RGB(r, g, b) =>
println!("Red: {}, green: {}, and blue: {}!", r, g, b),
Color::HSV(h, s, v) =>
println!("Hue: {}, saturation: {}, value: {}!", h, s, v),
Color::HSL(h, s, l) =>
println!("Hue: {}, saturation: {}, lightness: {}!", h, s, l),
Color::CMY(c, m, y) =>
println!("Cyan: {}, magenta: {}, yellow: {}!", c, m, y),
Color::CMYK(c, m, y, k) =>
println!("Cyan: {}, magenta: {}, yellow: {}, key (black): {}!",
c, m, y, k),
}
}
输出:
What color is it?
The color is Red!
解构指针和引用
对指针来说,解构(destructure)和解引用(dereference)要区分开,因为这两者的概念是不同的,和 C
那样的语言用法不一样。
- 解引用使用
*
- 解构使用
&
、ref
、和ref mut
Rust
fn main() {
// `&` 表示取引用。
let reference = &4;
match reference {
// 如果用 `&val` 这个模式去匹配 `reference`,就相当于做这样的比较:
// `&i32`(译注:即 `reference` 的类型)
// `&val`(译注:即用于匹配的模式)
// ^ 我们看到,如果去掉匹配的 `&`,`i32` 应当赋给 `val`。
// 译注:因此可用 `val` 表示被 `reference` 引用的值 4。
&val => println!("Got a value via destructuring: {:?}", val),
}
// 如果不想用 `&`,需要在匹配前解引用。
match *reference {
val => println!("Got a value via dereferencing: {:?}", val),
}
// 如果一开始就不用引用,会怎样? `reference` 是一个 `&` 类型,因为赋值语句
// 的右边已经是一个引用。但下面这个不是引用,因为右边不是。
let _not_a_reference = 3;
// Rust 对这种情况提供了 `ref`。它更改了赋值行为,从而可以对具体值创建引用。
// 下面这行将得到一个引用。
let ref _is_a_reference = 3;
// 相应地,定义两个非引用的变量,通过 `ref` 和 `ref mut` 仍可取得其引用。
let value = 5;
let mut mut_value = 6;
// 使用 `ref` 关键字来创建引用。
// 译注:下面的 r 是 `&i32` 类型,它像 `i32` 一样可以直接打印,因此用法上
// 似乎看不出什么区别。但读者可以把 `println!` 中的 `r` 改成 `*r`,仍然能
// 正常运行。前面例子中的 `println!` 里就不能是 `*val`,因为不能对整数解
// 引用。
match value {
ref r => println!("Got a reference to a value: {:?}", r),
}
// 类似地使用 `ref mut`。
match mut_value {
ref mut m => {
// 已经获得了 `mut_value` 的引用,先要解引用,才能改变它的值。
*m += 10;
println!("We added 10. `mut_value`: {:?}", m);
},
}
}
输出:
Got a value via destructuring: 4
Got a value via dereferencing: 4
Got a reference to a value: 5
We added 10. `mut_value`: 16
解构结构体
类似地,解构 struct
如下所示:
Rust
fn main() {
struct Foo { x: (u32, u32), y: u32 }
// 解构结构体的成员
let foo = Foo { x: (1, 2), y: 3 };
let Foo { x: (a, b), y } = foo;
println!("a = {}, b = {}, y = {} ", a, b, y);
// 可以解构结构体并重命名变量,成员顺序并不重要
let Foo { y: i, x: j } = foo;
println!("i = {:?}, j = {:?}", i, j);
// 也可以忽略某些变量
let Foo { y, .. } = foo;
println!("y = {}", y);
// 这将得到一个错误:模式中没有提及 `x` 字段
// let Foo { y } = foo;
}
输出:
a = 1, b = 2, y = 3
i = 3, j = (1, 2)
y = 3
卫语句
可以加上 match
卫语句(guard) 来过滤分支。
Rust
fn main() {
let pair = (2, -2);
// 试一试 ^ 将不同的值赋给 `pair`
println!("Tell me about {:?}", pair);
match pair {
(x, y) if x == y => println!("These are twins"),
// ^ `if` 条件部分是一个卫语句
(x, y) if x + y == 0 => println!("Antimatter, kaboom!"),
(x, _) if x % 2 == 1 => println!("The first one is odd"),
_ => println!("No correlation..."),
}
}
输出:
Tell me about (2, -2)
Antimatter, kaboom!
绑定
Rust
// `age` 函数,返回一个 `u32` 值。
fn age() -> u32 {
15
}
fn main() {
println!("Tell me what type of person you are");
match age() {
0 => println!("I haven't celebrated my first birthday yet"),
// 可以直接匹配(`match`) 1 ..= 12,但那样的话孩子会是几岁?
// 相反,在 1 ..= 12 分支中绑定匹配值到 `n` 。现在年龄就可以读取了。
n @ 1 ..= 12 => println!("I'm a child of age {:?}", n),
n @ 13 ..= 19 => println!("I'm a teen of age {:?}", n),
// 不符合上面的范围。返回结果。
n => println!("I'm an old person of age {:?}", n),
}
}
输出:
Tell me what type of person you are
I'm a teen of age 15
你也可以使用绑定来"解构" enum
变体,例如 Option
:
Rust
fn some_number() -> Option<u32> {
Some(42)
}
fn main() {
match some_number() {
// 得到 `Some` 可变类型,如果它的值(绑定到 `n` 上)等于 42,则匹配。
Some(n @ 42) => println!("The Answer: {}!", n),
// 匹配任意其他数字。
Some(n) => println!("Not interesting... {}", n),
// 匹配任意其他值(`None` 可变类型)。
_ => (),
}
}
输出:
The Answer: 42!
if let
在一些场合下,用 match
匹配枚举类型并不优雅。比如:
Rust
#![allow(unused)]
fn main() {
// 将 `optional` 定为 `Option<i32>` 类型
let optional = Some(7);
match optional {
Some(i) => {
println!("This is a really long string and `{:?}`", i);
// ^ 行首需要 2 层缩进。这里从 optional 中解构出 `i`。
// 译注:正确的缩进是好的,但并不是 "不缩进就不能运行" 这个意思。
},
_ => {},
// ^ 必须有,因为 `match` 需要覆盖全部情况。
};
}
输出:
This is a really long string and `7`
if let
在这样的场合要简洁得多,并且允许指明数种失败情形下的选项:
Rust
fn main() {
// 全部都是 `Option<i32>` 类型
let number = Some(7);
let letter: Option<i32> = None;
let emoticon: Option<i32> = None;
// `if let` 结构读作:若 `let` 将 `number` 解构成 `Some(i)`,则执行
// 语句块(`{}`)
if let Some(i) = number {
println!("Matched {:?}!", i);
}
// 如果要指明失败情形,就使用 else:
if let Some(i) = letter {
println!("Matched {:?}!", i);
} else {
// 解构失败。切换到失败情形。
println!("Didn't match a number. Let's go with a letter!");
};
// 提供另一种失败情况下的条件。
let i_like_letters = false;
if let Some(i) = emoticon {
println!("Matched {:?}!", i);
// 解构失败。使用 `else if` 来判断是否满足上面提供的条件。
} else if i_like_letters {
println!("Didn't match a number. Let's go with a letter!");
} else {
// 条件的值为 false。于是以下是默认的分支:
println!("I don't like letters. Let's go with an emoticon :)!");
};
}
同样,可以用 if let
匹配任何枚举值:
Rust
// 以这个 enum 类型为例
enum Foo {
Bar,
Baz,
Qux(u32)
}
fn main() {
// 创建变量
let a = Foo::Bar;
let b = Foo::Baz;
let c = Foo::Qux(100);
// 变量 a 匹配到了 Foo::Bar
if let Foo::Bar = a {
println!("a is foobar");
}
// 变量 b 没有匹配到 Foo::Bar,因此什么也不会打印。
if let Foo::Bar = b {
println!("b is foobar");
}
// 变量 c 匹配到了 Foo::Qux,它带有一个值,就和上面例子中的 Some() 类似。
if let Foo::Qux(value) = c {
println!("c is {}", value);
}
}
while let
和 if let
类似,while let
也可以把别扭的 match
改写得好看一些。考虑下面这段使 i
不断增加的代码:
Rust
#![allow(unused)]
fn main() {
// 将 `optional` 设为 `Option<i32>` 类型
let mut optional = Some(0);
// 重复运行这个测试。
loop {
match optional {
// 如果 `optional` 解构成功,就执行下面语句块。
Some(i) => {
if i > 9 {
println!("Greater than 9, quit!");
optional = None;
} else {
println!("`i` is `{:?}`. Try again.", i);
optional = Some(i + 1);
}
// ^ 需要三层缩进!
},
// 当解构失败时退出循环:
_ => { break; }
// ^ 为什么必须写这样的语句呢?肯定有更优雅的处理方式!
}
}
}
使用 while let
可以使这段代码变得更加优雅:
Rust
fn main() {
// 将 `optional` 设为 `Option<i32>` 类型
let mut optional = Some(0);
// 这读作:当 `let` 将 `optional` 解构成 `Some(i)` 时,就
// 执行语句块(`{}`)。否则就 `break`。
while let Some(i) = optional {
if i > 9 {
println!("Greater than 9, quit!");
optional = None;
} else {
println!("`i` is `{:?}`. Try again.", i);
optional = Some(i + 1);
}
// ^ 使用的缩进更少,并且不用显式地处理失败情况。
}
// ^ `if let` 有可选的 `else`/`else if` 分句,
// 而 `while let` 没有。
}