流程控制(Rust入门) 本文章参考自官方学习文档及社区学习文档以下是参考链接:
官方:
社区:
直接上手学习推荐:
概述点 任何编程语言都包含的一个必要部分就是改变控制流程:if
/else
,for
等。
本文内容:
if/else 条件判断
loop 循环
while 循环
for 循环和区间
match 匹配
if let
while let
if/else if
-else
分支判断和其他语言类似。与很多语言不同的是,Rust
语言中的布尔判断条件不必用小括号包住,且每个条件后面都跟着一个代码块。if
-else
条件选择是一个表达式,并且所有分支都必须返回相同的类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 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" ); 10 * n } else { println! (", and is a big number, half the number" ); n / 2 }; println! ("{} -> {}" , n, big_n); }
loop循环 Rust
提供了loop
关键字来实现一个无限循环。
可以使用 break
语句在任何时候退出一个循环,另外可用 continue
跳过循环体剩余部分并开始下一轮循环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 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 ; } } }
循环嵌套和标签 在处理嵌套循环的时候可以break
或continue
外层循环。在这类情形中,循环必须用一些'label
(标签)来注明,并且标签必须传递给 break
/continue
语句。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #![allow(unreachable_code)] fn main () { 'outer : loop { println! ("Entered the outer loop" ); 'inner : loop { println! ("Entered the inner loop" ); break 'outer ; } println! ("This point will never be reached" ); } println! ("Exited the outer loop" ); }
从loop循环中返回 loop
有个用途是尝试一个操作直到成功为止。若操作返回一个值,则可能需要将其传递给代码的其余部分:将该值放在 break
之后,他就会被 loop
表达式返回。
1 2 3 4 5 6 7 8 9 10 11 12 13 fn main () { let mut counter = 0 ; let result = loop { counter += 1 ; if counter == 10 { break counter * 2 ; } }; assert_eq! (result, 20 ); }
while 循环 while
关键字可以用做当型循环(当条件满足时循环)
让我们用 while
循环一个并不出名的FizzBuzz程序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 fn main () { let mut n = 1 ; 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); } n += 1 ; } }
for循环和区间 for in
结构可以遍历一个 Iterator
(迭代器)。创建迭代器的一个最简单的方式是使用取件标记 a..b
。 这会生成从 a
(包含此值)到 b
(不含此值)的,步长为1的一系列值。
让我们使用 for
代替 while
来写 FizzBuzz 程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 fn main () { 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
表示两端都包含在内的范围。上面的代码可以写成:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 fn main () { 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
互动。在迭代器 trait 一节将会谈到,如果没有特别指定,for
循环会对给出的集合应用 into_iter
函数,把它转换成一个迭代器。这并不是把集合变成迭代器的我一方法,其他的方法有 iter
和 iter_mut
函数。
这三个函数会以不同的方式返回集合中的数据。
iter
- 在每次迭代中借用集合中的一个元素。这样集合本身不会改变,循环之后仍可以使用。
1 2 3 4 5 6 7 8 9 10 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), } } }
译注:Ferris 是 Rust
的 非官方吉祥物 。
into_iter
- 会消耗集合。在每次迭代中,集合中的数据本身会被提供。一单集合被消耗了,之后就无法使用了,因为他已经在循环中销毁了
1 2 3 4 5 6 7 8 9 10 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), } } }
item_mut
- 可变的(mutably )借用集合中的每个元素,从而允许集合呗就地更改
1 2 3 4 5 6 7 8 9 10 11 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); }
在上面这些代码中,注意 match 的分支中所写的类型不同,这是不同迭代方式的关键区别。因为类型不同,能够执行的操作当然也不同。
match匹配 Rust
通过 match
关键字来提供模式匹配,用法和C语言的switch类似
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 fn main () { let number = 13 ; println! ("Tell me about {}" , number); match number { 1 => println! ("One!" ), 2 | 3 | 5 | 7 | 11 => println! ("This is a prime" ), 13 ...19 => println! ("A teen" ), _ => println! ("Ain't special" ), } let boolean = true ; let binary = match boolean { false => 0 , true => 1 , }; println! ("{} -> {}" , boolean, binary); }
解构 match
代码块能以多种方式解构物件
元组 元组可以再 match
中解构,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 fn main () { let pair = (0 , -2 ); println! ("Tell me about {:?}" , pair); match pair { (0 , y) => println! ("First is `0` and `y` is `{:?}`" , y), (x, 0 ) => println! ("`x` is `{:?}` and last is `0`" , x), _ => println! ("It doesn't matter what they are" ), } }
枚举 和前面类似,解构 enum
的方式如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #[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 ); println! ("What color is it?" ); 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), } }
指针和引用 对指针来说,解构(destructure
)和解引用(dereference
)要区分开,因为两者概念不相同,和 C
那样的语言用法不一样
解引用使用 *
解构使用 &
、ref
和 ref mut
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 fn main () { let reference = &4 ; match reference { &val => println! ("Got a value via destructuring: {:?}" , val), } match *reference { val => println! ("Got a value via dereferencing: {:?}" , val), } let _not_a_reference = 3 ; let ref _is_a_reference = 3 ; let value = 5 ; let mut mut_value = 6 ; match value { ref r => println! ("Got a reference to a value: {:?}" , r), } match mut_value { ref mut m => { *m += 10 ; println! ("We added 10. `mut_value`: {:?}" , m); }, } }
结构体 类似的,解构 struct
如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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); }
守卫 可以加上 match
守卫 (guard)来过滤分支
1 2 3 4 5 6 7 8 9 10 11 12 13 fn main () { let pair = (2 , -2 ); println! ("Tell me about {:?}" , pair); match pair { (x, y) if x == y => println! ("These are twins" ), (x, y) if x + y == 0 => println! ("Antimatter, kaboom!" ), (x, _) if x % 2 == 1 => println! ("The first one is odd" ), _ => println! ("No correlation..." ), } }
绑定 在 match
中,若简介的访问了一个变量,这不经过重新绑定就无法在分支中再使用它。 match
提供了 @
符号来绑定变量到名称:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 fn age () -> u32 { 15 } fn main () { println! ("Tell me type of person you are" ); match age () { 0 => println! ("I'm not born yet I guess" ), 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), } }
你也可以使用绑定来 [解构] enum
变体,例如 Option
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 fn some_number () -> Option <u32 > { Some (42 ) } fn main () { match some_number () { Some (n @ 42 ) => println! ("The Answer: {}!" , n), Some (n) => println! ("Not interesting... {}" , n), _ => (), } }
if let 某些场景下使用 match
匹配枚举类型并不优雅,比如:
1 2 3 4 5 6 7 8 9 10 11 12 let optional = Some (7 );match optional { Some (i) => { println! ("This is a really long string and `{:?}`" , i); }, _ => {}, };
if let
在这样的场景下要简洁的多,并且允许指明数种失败情形下的选项:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 fn main () { let number = Some (7 ); let letter : Option <i32 > = None ; let emoticon : Option <i32 > = None ; if let Some (i) = number { println! ("Matched {:?}!" , i); } 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 i_like_letters { println! ("Didn't match a number. Let's go with a letter!" ); } else { println! ("I don't like letters. Let's go with an emoticon :)!" ); }; }
同样,可以用 if let
匹配枚举值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 enum Foo { Bar, Baz, Qux (u32 ) } fn main () { let a = Foo::Bar; let b = Foo::Baz; let c = Foo::Qux (100 ); if let Foo ::Bar = a { println! ("a is foobar" ); } if let Foo ::Bar = b { println! ("b is foobar" ); } if let Foo ::Qux (value) = c { println! ("c is {}" , value); } }
另一个好处是:if let
允许匹配枚举非参数化的变量,即枚举未注明 #[derive(PartialEq)]
,我们也没有为其实现 PartialEq
。在这种情况下,通常 if Foo::Bar==a
会出错,因为此类枚举的实例不具有可比性。但是,if let
是可行的。
动手试一试例子:
请尝试使用 if let
修复下面示例
1 2 3 4 5 6 7 8 9 10 11 12 13 enum Foo {Bar}fn main () { let a = Foo::Bar; if Foo::Bar == a { println! ("a is foobar" ); } }
答案很简单就是你写的那个
while let 和 if let
类似,while let
也可以把别扭的 match
改写得好看一些。考虑下面这段使 i
不断增加的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 let mut optional = Some (0 );loop { match 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
可以使这段代码变得更加优雅:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 fn main () { let mut optional = Some (0 ); 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 ); } } }