控制流程

Rust 的某些控制流程結構與其他程式語言不同。這些結構會用於模式配對:

  • if let 運算式
  • while let 運算式
  • match 運算式

if let 運算式

if let 運算式可讓您根據值是否符合模式,執行不同的程式碼:

fn sleep_for(secs: f32) {
    let dur = if let Ok(dur) = std::time::Duration::try_from_secs_f32(secs) {
        dur
    } else {
        std::time::Duration::from_millis(500)
    };
    std::thread::sleep(dur);
    println!("slept for {:?}", dur);
}

fn main() {
    sleep_for(-10.0);
    sleep_for(0.8);
}

let else 運算式

如果是要配對模式並從函式傳回的常見情況,請使用 let else。如果是「其他」情況,則必須發散 (returnbreak 或恐慌,也就是落在區塊結尾之外的任何情況)。

fn hex_or_die_trying(maybe_string: Option<String>) -> Result<u32, String> {
    let s = if let Some(s) = maybe_string {
        s
    } else {
        return Err(String::from("got None"));
    };

    let first_byte_char = if let Some(first_byte_char) = s.chars().next() {
        first_byte_char
    } else {
        return Err(String::from("got empty string"));
    };

    if let Some(digit) = first_byte_char.to_digit(16) {
        Ok(digit)
    } else {
        Err(String::from("not a hex digit"))
    }
}

fn main() {
    println!("result: {:?}", hex_or_die_trying(Some(String::from("foo"))));
}

if let 的情況一樣,有一個 while let 變數可針對模式重複測試值:

fn main() {
    let mut name = String::from("Comprehensive Rust 🦀");
    while let Some(c) = name.pop() {
        println!("character: {c}");
    }
    // (There are more efficient ways to reverse a string!)
}

Here String::pop returns Some(c) until the string is empty, after which it will return None. The while let lets us keep iterating through all items.

This slide should take about 10 minutes.

if-let

  • Unlike match, if let does not have to cover all branches. This can make it more concise than match.
  • 常見用途是在使用 Option 時處理 Some 值。
  • match 不同,if let 不會為模式比對支援成立條件子句。

let-else

如上所示,if-let 可能會越加越多。let-else 結構支援壓平合併這個巢狀程式碼。請為學生重新編寫這個冗長的版本,讓他們見識改寫的效果。

重新編寫的版本如下:

#![allow(unused)]
fn main() {
fn hex_or_die_trying(maybe_string: Option<String>) -> Result<u32, String> {
    let Some(s) = maybe_string else {
        return Err(String::from("got None"));
    };

    let Some(first_byte_char) = s.chars().next() else {
        return Err(String::from("got empty string"));
    };

    let Some(digit) = first_byte_char.to_digit(16) else {
        return Err(String::from("not a hex digit"));
    };

    return Ok(digit);
}
}

while-let

  • 請指出只要值符合模式,while let 迴圈就會持續運作。
  • You could rewrite the while let loop as an infinite loop with an if statement that breaks when there is no value to unwrap for name.pop(). The while let provides syntactic sugar for the above scenario.