方法3:避免匹配Option和Result[方法1]阐述了枚举(enum)的优点,并展示了match表达式如何强制程序员考虑所有可能性;这个方法探讨了在某些情况下,你应尽量避免使用match表达式——至少是显式地。[方法1]还介绍了Rust标准库提供的两个无处不在
[方法 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 语言
):
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!