if let
简洁控制流
ch06-03-if-let.md
commit bb7e429ad6b59d9a0c37db7434976364cbb9c6da
if let
语法让我们以一种不那么冗长的方式结合 if
和 let
,来处理只匹配一个模式的值而忽略其他模式的情况。考虑示例 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}"), _ => (), } }
如果值是 Some
,我们希望打印出 Some
成员中的值,这个值被绑定到模式中的 max
变量里。对于 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
分支中一样。模式不匹配时 if let
块中的代码不会执行。
使用 if let
意味着编写更少代码,更少的缩进和更少的样板代码。然而,这样会失去 match
强制要求的穷尽性检查。match
和 if let
之间的选择依赖特定的环境以及增加简洁度和失去穷尽性检查的权衡取舍。
换句话说,可以认为 if let
是 match
的一个语法糖,它当值匹配某一模式时执行代码而忽略所有其他值。
可以在 if let
中包含一个 else
。else
块中的代码与 match
表达式中的 _
分支块中的代码相同,这样的 match
表达式就等同于 if let
和 else
。回忆一下示例 6-4 中 Coin
枚举的定义,其 Quarter
成员也包含一个 UsState
值。如果想要计数所有不是 25 美分的硬币的同时也报告 25 美分硬币所属的州,可以使用这样一个 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 let
和 else
表达式:
#[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>
类型是如何帮助你利用类型系统来避免出错的。当枚举值包含数据时,你可以根据需要处理多少情况来选择使用 match
或 if let
来获取并使用这些值。
你的 Rust 程序现在能够使用结构体和枚举在自己的作用域内表现其内容了。在你的 API 中使用自定义类型保证了类型安全:编译器会确保你的函数只会得到它期望的类型的值。
为了向你的用户提供一个组织良好的 API,它使用起来很直观并且只向用户暴露他们确实需要的部分,那么现在就让我们转向 Rust 的模块系统吧。