使用 if let 实现简洁的控制流

if let 语法让你可以将 iflet 结合起来,以一种更简洁的方式处理匹配某个模式的值,同时忽略其他值。考虑列表 6-6 中的程序,该程序在 config_max 变量上匹配一个 Option<u8> 值,但只有当值是 Some 变体时才执行代码。

fn main() {
    let config_max = Some(3u8);
    match config_max {
        Some(max) => println!("The maximum is configured to be {max}"),
        _ => (),
    }
}
Listing 6-6: A match that only cares about executing code when the value is Some

如果值是 Some,我们通过在模式中将值绑定到变量 max 来打印 Some 变体中的值。我们不想对 None 值做任何处理。为了满足 match 表达式,我们必须在处理一个变体后添加 _ => (),这添加了一些令人烦恼的样板代码。

相反,我们可以使用 if let 以更简洁的方式编写。以下代码的行为与列表 6-6 中的 match 相同:

fn main() {
    let config_max = Some(3u8);
    if let Some(max) = config_max {
        println!("The maximum is configured to be {max}");
    }
}

if let 语法接受一个模式和一个表达式,二者由等号分隔。它的工作方式与 match 相同,其中表达式被传递给 match,而模式是其第一个分支。在这种情况下,模式是 Some(max)max 绑定到 Some 内的值。然后我们可以在 if let 块的主体中使用 max,就像我们在相应的 match 分支中使用 max 一样。如果值不匹配模式,则 if let 块中的代码不会运行。

使用if let意味着更少的输入,更少的缩进,以及更少的样板代码。然而,你会失去match强制的详尽检查。选择matchif let取决于你在特定情况下的操作,以及获得简洁性是否是失去详尽检查的合适权衡。

换句话说,你可以将if let视为一种match的语法糖,它在值匹配一个模式时运行代码,然后忽略所有其他值。

我们可以包含一个 elseif let 一起使用。与 else 一起的代码块与等效于 if letelsematch 表达式中的 _ 情况下的代码块相同。回想一下在清单 6-4 中的 Coin 枚举定义,其中 Quarter 变体还包含一个 UsState 值。如果我们想计算所有非季度硬币,同时宣布季度硬币的州,我们可以使用一个 match 表达式,如下所示:

#[derive(Debug)]
enum UsState {
    Alabama,
    Alaska,
    // --snip--
}

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter(UsState),
}

fn main() {
    let coin = Coin::Penny;
    let mut count = 0;
    match coin {
        Coin::Quarter(state) => println!("State quarter from {state:?}!"),
        _ => count += 1,
    }
}

或者我们可以使用 if letelse 表达式,如下所示:

#[derive(Debug)]
enum UsState {
    Alabama,
    Alaska,
    // --snip--
}

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter(UsState),
}

fn main() {
    let coin = Coin::Penny;
    let mut count = 0;
    if let Coin::Quarter(state) = coin {
        println!("State quarter from {state:?}!");
    } else {
        count += 1;
    }
}

如果您遇到程序中的逻辑过于冗长,无法用 match 表达的情况,请记住 if let 也是您 Rust 工具箱中的一部分。

摘要

我们现在介绍了如何使用枚举来创建可以是枚举值集之一的自定义类型。我们展示了标准库中的Option<T>类型如何帮助你利用类型系统来防止错误。当枚举值包含数据时,你可以根据需要处理的案例数量,使用matchif let来提取和使用这些值。

您的 Rust 程序现在可以使用结构体和枚举来表达领域中的概念。创建自定义类型以在您的 API 中使用可以确保类型安全:编译器将确保您的函数只接收每个函数期望的类型的值。

为了向用户提供一个组织良好且易于使用的API,并且只暴露用户需要的内容,现在让我们转向Rust的模块。