if let简单控制流

ch06-03-if-let.md
commit 3f2a1bd8dbb19cc48b210fc4fb35c305c8d81b56

if let语法让我们以一种不那么冗长的方式结合iflet,来处理匹配一个模式的值而忽略其他的值。考虑列表 6-6 中的程序,它匹配一个Option<u8>值并只希望当值是三时执行代码:

let some_u8_value = Some(0u8);
match some_u8_value {
    Some(3) => println!("three"),
    _ => (),
}

Listing 6-6: A match that only cares about executing code when the value is Some(3)

我们想要对Some(3)匹配进行操作不过不想处理任何其他Some<u8>值或None值。为了满足match表达式(穷尽性)的要求,必须在处理完这唯一的成员后加上_ => (),这样也要增加很多样板代码。

不过我们可以使用if let这种更短的方式编写。如下代码与列表 6-6 中的match行为一致:

# let some_u8_value = Some(0u8);
if let Some(3) = some_u8_value {
    println!("three");
}

if let获取通过=分隔的一个模式和一个表达式。它的工作方式与match相同,这里的表达式对应match而模式则对应第一个分支。

使用if let意味着编写更少代码,更少的缩进和更少的样板代码。然而,这样会失去match强制要求的穷尽性检查。matchif let之间的选择依赖特定的环境以及增加简洁度和失去穷尽性检查的权衡取舍。

换句话说,可以认为if letmatch的一个语法糖,它当值匹配某一模式时执行代码而忽略所有其他值。

可以在if let中包含一个elseelse块中的代码与match表达式中的_分支块中的代码相同,这样的match表达式就等同于if letelse。回忆一下列表 6-4 中Coin枚举的定义,它的Quarter成员包含一个UsState值。如果想要计数所有不是 25 美分的硬币的同时也报告 25 美分硬币所属的州,可以使用这样一个match表达式:

# #[derive(Debug)]
# enum UsState {
#    Alabama,
#    Alaska,
# }
#
# enum Coin {
#    Penny,
#    Nickel,
#    Dime,
#    Quarter(UsState),
# }
# 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,
# }
#
# enum Coin {
#    Penny,
#    Nickel,
#    Dime,
#    Quarter(UsState),
# }
# 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 的模块系统吧。