读Rust By Example(7)

流控制

if/else

与其他语言不同的是,布尔条件不需要包含在小括号内,每一个条件都要跟随一个代码块。 if-else 条件是表达式,所有的的分支都必须返回相同的值。

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, reduce by two");
            n / 2
        };
    //   ^ 别忘记要加一个分号,所有的绑定语句都要加上分号

    println!("{} -> {}", n, big_n);
}

循环

Rust提供 loop 关键字来指定无限循环。

break 语句可在任意时刻中断循环, continue 语句能跳过该循环剩余部分并继续执行下一循环。

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;
        }
    }
}
嵌套循环和标签

在执行内部循环的时候是可以 continuebreak 外部循环的。在此例中,循环要由一些 'label 指定,并且标签要传给 breakcontinue 语句。

#![allow(unreachable_code)]

fn main() {  
    'outer: loop {
        println!("Entered the outer loop");
        'inner: loop {
            println!("Entered the inner loop");
            // This would break only the inner loop
            //break;
            // This breaks the outer loop
            break 'outer;
        }
        println!("This point will never be reached");
    }
    println!("Exited the outer loop");
}

while

while 关键字能够在满足特定条件的时候停止循环。

应用 while 循环写一个 Fizz Buzz

Players take turns to count incrementally, replacing any number divisible by three with the word "fizz", and any number divisible by five with the word "buzz".

fn main() {  
    let mut i = 1;
    while i != 100 {
        if i % 15 == 0 {
            println!("Fizzbuzz");
        }
        else if i % 3  == 0{
            println!("Fizz");
        }
        else if i % 5 == 0{
            println!("Buzz");
        }
        else {
            println!("{}", i);
        }
        i += 1;
    }
}

for 和范围

for 结构能够用于迭代迭代器。构造迭代器最简单的方法就是使用范围 a..b。范围包含 a 但不包含 b

for 循环写一个 FizzBuzz

fn main() {  
    let mut i = 1;

    for i in 1..101 {
        if i % 15 == 0 {
            println!("Fizzbuzz");
        }
        else if i % 3  == 0{
            println!("Fizz");
        }
        else if i % 5 == 0{
            println!("Buzz");
        }
        else {
            println!("{}", i);
        }
    }
}

匹配

Rust通过 match 关键字提供了模式匹配,好比C语言中的 switch 关键字。

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;
    // Match is an expression too
    let binary = match boolean {
        // 必须覆盖分支所有可能性
        false => 0,
        true => 1,
    };

    println!("{} -> {}", boolean, binary);
}
析构

match 代码块能以析构多种结构。

元组

析构成元组:

fn main() {  
    let pair = (0, -2);
    println!("Tell me about {:?}", pair);
    // 用于析构元组
    match pair {
        // Destructure the second
        (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"),
        // `_` means don't bind the value to a variable
    }
}
枚举
#[allow(dead_code)]
enum Color {  
    // These 3 are specified solely by their name.
    Red,
    Blue,
    Green,
    // These likewise tie `u32` tuples to different names: color models.
    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);
    // TODO ^ Try different variants for `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),
        // Don't need another arm because all variants have been examined
    }
}
指针/引用

对于指针,析构和解引用是和C语言中不同的概念。

  • 解引用用 *
  • 析构用 &, ref, 及 ref mut
fn main() {  
    // 分配一个 `i32` 类型的引用. `&` 说明有一个引用被分配。
    let reference = &4;

    match reference {
        // 如果 `reference`s 匹配为 `&val`, 会产生如下的比较
        // `&i32`
        // `&val`
        // 我们看到,如果匹配的 `&` 都去掉了,那么就是 `i32` 赋给 `val`。
        &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` 关键字创建引用
    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);
        },
    }
}
结构

同样的,结构也可以被析构:

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;
}
守卫

match 守卫能添加用于过滤分支。

fn main() {  
    let pair = (2, -2);
    println!("Tell me about {:?}", pair);
    match pair {
        (x, y) if x == y => println!("These are twins"),
        // The ^ `if condition` part is a guard
        (x, y) if x + y == 0 => println!("Antimatter, kaboom!"),
        (x, _) if x % 2 == 1 => println!("The first one is odd"),
        _ => println!("No correlation..."),
    }
}
绑定

间接地访问一个变量不可能在分支中使用这个没有重新绑定的变量。 match 提供了 @ 符号来绑定变量到名称:

// `age` 函数,返回一个 `u32` 值。
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"),
        // 不能直接 `匹配(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),
    }
}

if let

在一些情况下, match 显得很笨拙。

// 使 `optional` 使用类型 `Option<i32>`
let optional = Some(7);

match optional {  
    Some(i) => {
        println!("This is a really long string and `{:?}`", i);
        // ^ 行首需要2个缩进,就这样可以从 option 类型中对 `i`
        // 进行解构
    },
    _ => {},
    // ^ 必需内容,因为 `match` 需要覆盖全部情况。难道不觉得冗余吗?
};

if let 对这样的用法要简洁得多,并且允许指明特定的各种不同的失败可选项 内容(options):

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 {
        // 解构失败。换到失败情形(Change to the failure case)。
        println!("Didn't match a number. Let's go with a letter!");
    };

    // 提供一个改变的失败条件(Provide an altered failing condition)。
    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 {
        // 条件执行错误。这是默认的分支:
        println!("I don't like letters. Let's go with an emoticon :)!");
    };
}

while let

if let 类似,while let 会产生更加难看的 match 的一连串内容。 考虑下面的有关增量 i 的一连串内容:

// 将 `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 可以使这一连串内容变得更加优雅:

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` 没有。
}