《Effective Rust》方法 3:避免匹配 Option 和 Result

  • King
  • 更新于 2024-04-21 06:28
  • 阅读 155

方法3:避免匹配Option和Result[方法1]阐述了枚举(enum)的优点,并展示了match表达式如何强制程序员考虑所有可能性;这个方法探讨了在某些情况下,你应尽量避免使用match表达式——至少是显式地。[方法1]还介绍了Rust标准库提供的两个无处不在

方法 3:避免匹配 Option 和 Result

[方法 1] 阐述了枚举(enum)的优点,并展示了 match 表达式如何强制程序员考虑所有可能性;这个方法探讨了在某些情况下,你应尽量避免使用 match 表达式 —— 至少是显式地。

[方法 1] 还介绍了 Rust 标准库提供的两个无处不在的枚举:

  • Option<T>,表示一个值(类型为 T)可能存在也可能不存在。
  • Result<T, E>,用于当尝试返回一个值(类型为 T)的操作可能失败,并可能返回一个错误(类型为 E)。

对于这些特定的枚举,显式使用 match 通常会导致代码比实际需要的不够紧凑,而且不符合 Rust 的习惯用法。

第一种不需要使用 match 的情况是,当只关心值本身,而值的缺失(以及任何相关的错误)可以被忽略时。

struct S {
    field: Option<i32>,
}

let s = S { field: Some(42) };
match &s.field {
    Some(i) => println!("field is {}", i),
    None => {}
}

对于这种情况,使用 if let 表达式可以缩短一行代码,而且更重要的是,它的表达更清晰:

if let Some(i) = &s.field {
    println!("field is {}", i);
}

然而,大多数时候,值的缺失以及相关的错误是程序员必须处理的问题。设计软件以应对失败路径是困难的,大多数情况下这是无法通过语法支持减少的固有复杂性 —— 特别是,决定如果操作失败应该发生什么。

在某些情况下,正确的决定是执行一种鸵鸟策略,明确不处理失败。如果使用显式的 match 来这样做,会显得不必要的冗长:

let result = std::fs::File::open("/etc/passwd");
let f = match result {
    Ok(f) => f,
    Err(_e) => panic!("Failed to open /etc/passwd!"),
};

尽管如此,要明确一点:这些辅助函数仍然会引发 panic!,所以选择使用它们与选择直接 panic!([方法 18])是一样的。

然而,在许多情况下,正确的错误处理决策是将决策推迟给其他人。这在编写库时尤其正确,因为库的代码可能会在库作者无法预见的各种不同环境中使用。为了使其他人的工作更容易,即使这可能涉及不同错误类型之间的转换([方法 4]),也更倾向于使用 Result 而不是 Option

Result 也有一个 [#must_use] 属性,用来引导库用户朝着正确的方向前进 —— 如果使用返回的 Result 的代码忽略了它,编译器将生成一个警告:

warning: unused `Result` that must be used
  --> transform/src/main.rs:32:5
   |
32 |     f.set_len(0); // Truncate the file
   |     ^^^^^^^^^^^^^
   |
   = note: `#[warn(unused_must_use)]` on by default
   = note: this `Result` may be an `Err` variant, which should be handled

显式使用 match 可以让错误传播,但代价是增加了一些可见的样板代码(让人联想到 Go 语言):

剩余50%的内容订阅专栏后可查看

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
King
King
0x56af...a0dd
江湖只有他的大名,没有他的介绍。