控制流程

YouTube 上觀看本章內容: Part 1Part 2

控制流程(control flow)的意思是告訴你的程式碼在不同的情況下該怎麼做。最簡單的控制流程是 if

fn main() {
    let my_number = 5;
    if my_number == 7 {
        println!("It's seven");
    }
}

另外注意,你用的是 == 而不是 === 是用來比較的,= 是用來賦值的(給一個值)。另外注意,我們寫的是 if my_number == 7 而不是 if (my_number == 7)。在 Rust 中,你不需要在 if 條件用括號。

else ifelse 給你更多的控制:

fn main() {
    let my_number = 5;
    if my_number == 7 {
        println!("It's seven");
    } else if my_number == 6 {
        println!("It's six")
    } else {
        println!("It's a different number")
    }
}

印出 It's a different number,因為它不等於 7 或 6。

您可以使用 &&(和)和 ||(或)來新增更多條件。

fn main() {
    let my_number = 5;
    if my_number % 2 == 1 && my_number > 0 { // % 2 表示除以2之後的餘下的數
        println!("It's a positive odd number");
    } else if my_number == 6 {
        println!("It's six")
    } else {
        println!("It's a different number")
    }
}

印出 It's a positive odd number,因為當你把它除以 2 時,你有餘數 1,且它大於0。

你可以看到,過多的 ifelseelse if 會很難讀。在這種情況下,你可以使用 match 來代替,它看起來更乾淨。但是您必須為每一個可能的結果進行匹配(match)。例如,這將無法運作:

fn main() {
    let my_number: u8 = 5;
    match my_number {
        0 => println!("it's zero"),
        1 => println!("it's one"),
        2 => println!("it's two"),
        // ⚠️
    }
}

編譯器說:

error[E0004]: non-exhaustive patterns: `3u8..=std::u8::MAX` not covered
 --> src\main.rs:3:11
  |
3 |     match my_number {
  |           ^^^^^^^^^ pattern `3u8..=std::u8::MAX` not covered

這就意味著"你告訴我 0 到 2,但 u8 可以到 255。那 3 呢?4 呢?5 呢?"以此類推。所以你可以加上 _,意思是"其他任何東西"。

fn main() {
    let my_number: u8 = 5;
    match my_number {
        0 => println!("it's zero"),
        1 => println!("it's one"),
        2 => println!("it's two"),
        _ => println!("It's some other number"),
    }
}

印出 It's some other number

記住這些匹配的規則:

  • 你寫下 match,然後做一個 {} 程式碼區塊。
  • 在左邊寫上模式,用 => 胖箭頭說明匹配時該怎麼做。
  • 每一行稱為一個"分支(arm)"。
  • 在分支之間放一個逗號(不是分號)。

你可以用匹配結果來宣告一個值:

fn main() {
    let my_number = 5;
    let second_number = match my_number {
        0 => 0,
        5 => 10,
        _ => 2,
    };
}

second_number 將是 10。你看到最後的分號了嗎?那是因為,在 match 結束後,我們實際上告訴了編譯器這個資訊:let second_number = 10;

你也可以在更復雜的事情上進行匹配。你要用元組來做到。

fn main() {
    let sky = "cloudy";
    let temperature = "warm";

    match (sky, temperature) {
        ("cloudy", "cold") => println!("It's dark and unpleasant today"),
        ("clear", "warm") => println!("It's a nice day"),
        ("cloudy", "warm") => println!("It's dark but not bad"),
        _ => println!("Not sure what the weather is."),
    }
}

印出 It's dark but not bad,因為它與 skytemperature 的 "cloudy" 和 "warm" 相匹配。

你甚至可以把 if 放在 match 裡面。這稱為 "match guard":

fn main() {
    let children = 5;
    let married = true;

    match (children, married) {
        (children, married) if married == false => println!("Not married with {} children", children),
        (children, married) if children == 0 && married == true => println!("Married but no children"),
        _ => println!("Married? {}. Number of children: {}.", married, children),
    }
}

這將印出 Married? true. Number of children: 5.

在匹配時,你可以隨意多次使用 _。在這個關於顏色的匹配中,我們有三個顏色,但一次只能選中一個。

fn match_colours(rbg: (i32, i32, i32)) {
    match rbg {
        (r, _, _) if r < 10 => println!("Not much red"),
        (_, b, _) if b < 10 => println!("Not much blue"),
        (_, _, g) if g < 10 => println!("Not much green"),
        _ => println!("Each colour has at least 10"),
    }
}

fn main() {
    let first = (200, 0, 0);
    let second = (50, 50, 50);
    let third = (200, 50, 0);

    match_colours(first);
    match_colours(second);
    match_colours(third);

}

印出:

Not much blue
Each colour has at least 10
Not much green

這也說明了 match 陳述式的作用,因為在第一個例子中,它只印了 Not much blue。但是 first 也沒有多少綠色。match 陳述式總是在找到一個匹配項時停止,而不檢查其他的。這就是程式碼編譯得很好,但不是你想要的程式碼的一個好例子。

你可以做一個非常大的 match 陳述式來解決這個問題,但是使用 for 迴圈(loop)可能更好。我們將很快會討論到迴圈。

匹配必須回傳相同的型別。所以你不能這樣做:

fn main() {
    let my_number = 10;
    let some_variable = match my_number {
        10 => 8,
        _ => "Not ten", // ⚠️
    };
}

編譯器告訴你:

error[E0308]: `match` arms have incompatible types
  --> src\main.rs:17:14
   |
15 |       let some_variable = match my_number {
   |  _________________________-
16 | |         10 => 8,
   | |               - this is found to be of type `{integer}`
17 | |         _ => "Not ten",
   | |              ^^^^^^^^^ expected integer, found `&str`
18 | |     };
   | |_____- `match` arms have incompatible types

這樣也不行,原因同上。

fn main() {
    let some_variable = if my_number == 10 { 8 } else { "something else "}; // ⚠️
}

但是這樣就可以了,因為不是 match,所以你每次都有不同的 let 陳述式:

fn main() {
    let my_number = 10;

    if my_number == 10 {
        let some_variable = 8;
    } else {
        let some_variable = "Something else";
    }
}

你也可以使用 @match 表示式的值命名,然後你就可以使用它。在這個範例中,我們在函式中匹配 i32 輸入。如果是 4 或 13,我們要在 println! 陳述式中使用這個數字。否則,我們不需要使用它。

fn match_number(input: i32) {
    match input {
    number @ 4 => println!("{} is an unlucky number in China (sounds close to 死)!", number),
    number @ 13 => println!("{} is unlucky in North America, lucky in Italy! In bocca al lupo!", number),
    _ => println!("Looks like a normal number"),
    }
}

fn main() {
    match_number(50);
    match_number(13);
    match_number(4);
}

印出:

Looks like a normal number
13 is unlucky in North America, lucky in Italy! In bocca al lupo!
4 is an unlucky number in China (sounds close to 死)!