if let

在一些场合下,用 match 匹配枚举类型并不优雅。比如:

  1. // 将 `optional` 定为 `Option<i32>` 类型
  2. let optional = Some(7);
  3. match optional {
  4. Some(i) => {
  5. println!("This is a really long string and `{:?}`", i);
  6. // ^ 行首需要 2 层缩进。这里从 optional 中解构出 `i`。
  7. // 译注:正确的缩进是好的,但并不是 “不缩进就不能运行” 这个意思。
  8. },
  9. _ => {},
  10. // ^ 必须有,因为 `match` 需要覆盖全部情况。不觉得这行很多余吗?
  11. };

if let 在这样的场合要简洁得多,并且允许指明数种失败情形下的选项:

  1. fn main() {
  2. // 全部都是 `Option<i32>` 类型
  3. let number = Some(7);
  4. let letter: Option<i32> = None;
  5. let emoticon: Option<i32> = None;
  6. // `if let` 结构读作:若 `let` 将 `number` 解构成 `Some(i)`,则执行
  7. // 语句块(`{}`)
  8. if let Some(i) = number {
  9. println!("Matched {:?}!", i);
  10. }
  11. // 如果要指明失败情形,就使用 else:
  12. if let Some(i) = letter {
  13. println!("Matched {:?}!", i);
  14. } else {
  15. // 解构失败。切换到失败情形。
  16. println!("Didn't match a number. Let's go with a letter!");
  17. };
  18. // 提供另一种失败情况下的条件。
  19. let i_like_letters = false;
  20. if let Some(i) = emoticon {
  21. println!("Matched {:?}!", i);
  22. // 解构失败。使用 `else if` 来判断是否满足上面提供的条件。
  23. } else if i_like_letters {
  24. println!("Didn't match a number. Let's go with a letter!");
  25. } else {
  26. // 条件的值为 false。于是以下是默认的分支:
  27. println!("I don't like letters. Let's go with an emoticon :)!");
  28. };
  29. }

同样,可以用 if let 匹配任何枚举值:

  1. // 以这个 enum 类型为例
  2. enum Foo {
  3. Bar,
  4. Baz,
  5. Qux(u32)
  6. }
  7. fn main() {
  8. // 创建变量
  9. let a = Foo::Bar;
  10. let b = Foo::Baz;
  11. let c = Foo::Qux(100);
  12. // 变量 a 匹配到了 Foo::Bar
  13. if let Foo::Bar = a {
  14. println!("a is foobar");
  15. }
  16. // 变量 b 没有匹配到 Foo::Bar,因此什么也不会打印。
  17. if let Foo::Bar = b {
  18. println!("b is foobar");
  19. }
  20. // 变量 c 匹配到了 Foo::Qux,它带有一个值,就和上面例子中的 Some() 类似。
  21. if let Foo::Qux(value) = c {
  22. println!("c is {}", value);
  23. }
  24. }

另一个好处是:if let 允许匹配枚举非参数化的变量,即枚举未注明 #[derive(PartialEq)],我们也没有为其实现 PartialEq。在这种情况下,通常 if Foo::Bar==a 会出错,因为此类枚举的实例不具有可比性。但是,if let 是可行的。

你想挑战一下吗?使用 if let修复以下示例:

  1. // 该枚举故意未注明 `#[derive(PartialEq)]`,
  2. // 并且也没为其实现 `PartialEq`。这就是为什么下面比较 `Foo::Bar==a` 会失败的原因。
  3. enum Foo {Bar}
  4. fn main() {
  5. let a = Foo::Bar;
  6. // 变量匹配 Foo::Bar
  7. if Foo::Bar == a {
  8. // ^-- 这就是编译时发现的错误。使用 `if let` 来替换它。
  9. println!("a is foobar");
  10. }
  11. }

参见:

枚举Option,和相关的 RFC