Rust 流程控制

花落花開99發表於2020-12-19

條件選擇

if 條件選擇是一個表示式(可以用來賦值),並且所有分支都必須返回相同的型別。
判斷條件不必用小括號括起來,條件後跟的程式碼塊必須用大括號括起來。

示例一:if 表示式

fn main() {
    let number = 3;

    if number != 0 {
        println!("number was something other than zero");
    }
}

示例二:if-else 表示式

fn main() {
    let number = 3;

    if number < 5 {
        println!("condition was true");
    } else {
        println!("condition was false");
    }
}

示例三:if-else-if 表示式

fn main() {
    let n = 5;

    if n < 0 {
        println!("{} is negative", n);
    } else if n > 0 {
        println!("{} is positive", n);
    } else {
        println!("{} is zero", n);
    }
}

迴圈語句

Rust 提供了三種迴圈:loopwhilefor

loop 迴圈

無限迴圈,一般配合break表示式使用。

示例

fn main() {
    // 這是一個無限迴圈
    loop {
        println!("again!");
    }

    let mut counter = 0;

    let result = loop {
        counter += 1;
        // 設定迴圈終止條件,並返回一個結果
        if counter == 10 {
            break counter * 2;
        }
    };

    println!("The result is {}", result);
}

while 迴圈

示例

fn main() {
    let mut number = 3;

    while number != 0 {
        println!("{}!", number);、
        number = number - 1;
    }

    println!("LIFTOFF!!!");
}

for 迴圈

示例

fn main() {
    // `n` 的值為: 1, 2, ..., 100(`1..101` 等價於 `1..=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);
        }
    }

    let a = [10, 20, 30, 40, 50];
    // 使用 for 迴圈遍歷集合中的元素
    for element in a.iter() {
        println!("the value is: {}", element);
    }
}

模式匹配

Rust 通過 match 關鍵字提供了模式匹配功能,類似於 C 中的 switch,但功能更強大。

match 表示式由 match 關鍵字、用於匹配的值和一個或多個分支構成,每個分支包含一個模式(pattern)以及一個待執行的表示式。

match VALUE {
    PATTERN1 => EXPRESSION1,
    PATTERN2 => EXPRESSION2,
    PATTERN3 => EXPRESSION3,
    ...
    PATTERNn => EXPRESSIONn,
}

執行過程:依次將 PATTERN1PATTERN2… 和 VALUE 進行比對,一旦匹配成功就執行該分支中 => 後面的表示式,並且不再繼續匹配後面的分支。其中,PATTERNn 可以是字面量、元組、列舉、結構體、萬用字元、函式等。

模式匹配的要求:

  • 所有分支必須窮舉所有的可能。
  • 每個分支必須是一個表示式,且通常這些表示式的返回值型別必須相同。

示例

enum Direction {
    East,
    West,
    North,
    South,
}
fn main() {
    let d_west = Direction::West;
    let d_str = match d_west {
        Direction::East => "East",
        Direction::North | Direction::South => {
            panic!("South or North");
        }, // 該分支一定會觸發 panic,返回值型別可以與其他分支不同
        _ => "West",
    };
    println!("{}", d_str);
}

解構

當待匹配的模式是複合型別時,可以提取複合型別值中的部分資料。

示例一:解構元組

fn main() {
    let triple = (0, -2, 3);

    println!("Tell me about {:?}", triple);

    match triple {
        // 解構元組中第2和第3個元素
        (0, y, z) => println!("First is `0`, `y` is {:?}, and `z` is {:?}", y, z),
        // 使用 `..` 忽略其他元素,僅匹配第1個元素
        (1, ..)  => println!("First is `1` and the rest doesn't matter"),
        // `_` 匹配所有值,通常將它放在最後,用來匹配剩餘的所有可能值
        _      => println!("It doesn't matter what they are"),
    }
}

示例二:解構列舉

#[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),
    }
}

示例三:解構結構體

fn main() {
    struct Foo {
        x: (u32, u32),
        y: u32,
    }

    let foo = Foo { x: (1, 2), y: 3 };

    match foo {
        Foo { x: (1, b), y } => println!("First of x is 1, b = {},  y = {} ", b, y),

        // 可以對欄位(field)重新命名
        Foo { y: 2, x: i } => println!("y is 2, i = {:?}", i),
        // Foo { y: 2, x } => println!("y is 2, i = {:?}", x),

        // 可以使用 `..` 忽略一些欄位(field)
        Foo { y, .. } => println!("y = {}, we don't care about x", y),
    }
}

示例四:匹配範圍

fn main() {
    let x = 1;
    match x {
        1 ..=10 => println!("一到十"),
        _ => println!("其它"),
    }

    let c = 'w';
    match c {
        'a' ..='z' => println!("小寫字母"),
        'A' ..='Z' => println!("大寫字母"),
        _ => println!("其他字元"),
    }
}

示例五:多重匹配

fn main() {
    let x = 1;
    // 匹配多個模式時,使用 `|` 分隔
    match x {
        1 | 2 => println!("一或二"),
        _ => println!("其他"),
    }
}

後置條件

一個 if 表示式可以被放在 match 的模式之後,被稱為 match guard

示例

fn main() {
    let x = 4;
    let y = false;
    match x {
        4 | 5 if y => println!("yes"),
        _ => println!("no"),
    }
}

繫結

可以使用 @ 符號將模式中匹配的值繫結一個名稱,詳細說明見Identifier patterns

示例

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"),
        n @ 1  ..= 12 => println!("I'm a child of age {:?}", n),
        n @ 13 ..= 19 => println!("I'm a teen of age {:?}", n),
        // Nothing bound. Return the result.
        n             => println!("I'm an old person of age {:?}", n),
    }
}

if let

在某些情況下,可以使用 if let 對模式匹配進行簡化。

示例

fn main() {
    let number = Some(7);
    let letter: Option<i32> = None;

    match number {
        Some(i) => {
            println!("Matched {:?}!", 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!");
    }
}

while let

if let 類似,使用 while let 也可以對模式匹配進行簡化,只關注要匹配的值,其它的值不用做顯式處理。

示例

fn main() {
    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;
            }
        }
    }

    // 上面loop中的模式匹配可以改為下面這種形式
    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);
        }
    }
}

相關資料

The Rust Programming Language

The Rust Reference

Rust by Example

相關文章