来自其他功能语言(并且是Rust新手),我对Rust if let
语法的动机感到有些惊讶。 RFC提及没有if let
,今天的惯用解决方案,用于测试和展开Option<T>
&#34;是
match opt_val {
Some(x) => {
do_something_with(x);
}
None => {}
}
或
if opt_val.is_some() {
let x = opt_val.unwrap();
do_something_with(x);
}
在Scala中,可以完全相同,但惯用的解决方案是map
而不是Option
(或foreach
,如果它仅用于侧面doing_something_with(x)
)的效果。
为什么在Rust中做同样的事情并不是惯用的解决方案?
opt_val.map(|x| do_something_with(x));
答案 0 :(得分:19)
map()
用于转换可选值,而if let
主要用于执行副作用。虽然Rust不是纯语言,但是它的任何代码块都可以包含副作用,因此映射语义仍然存在。使用map()
执行副作用虽然可能,但只会让读者感到困惑。请注意,它不应该有性能损失,至少在简单的代码中 - LLVM优化器完全能够将闭包直接内联到调用函数中,因此它转而等同于match
语句。
在if let
之前,对Option
执行副作用的唯一方法是匹配或if
进行Option::is_some()
检查。 match
方法是最安全的方法,但它非常冗长,特别是当需要大量嵌套检查时:
match o1 {
Some(v1) => match v1.f {
Some(v2) => match some_function(v2) {
Some(r) => ...
None => {}
}
None => {}
}
None => {}
}
注意突出的向右漂移和许多语法噪音。如果分支不是简单匹配,而是具有多个语句的适当块,则情况会变得更糟。
另一方面, if option.is_some()
方法略显冗长但仍然读得非常糟糕。此外,它的条件检查和unwrap()
不是静态绑定的,所以如果编译器没有注意到它就可能出错。
if let
基于与match
相同的模式匹配基础结构解决了详细程度问题(因此它比if option.is_some()
更难出错),并且作为附带好处,允许使用模式中的任意类型,而不仅仅是Option
。例如,某些类型可能不提供类似map()
的方法; if let
仍然可以很好地与他们合作。所以if let
是明显的胜利,因此它是惯用的。
答案 1 :(得分:14)
.map()
特定于Option<T>
类型,但if let
(和while let
!)是适用于所有Rust类型的功能。
答案 2 :(得分:1)
因为你的解决方案创建了一个使用资源的闭包,而if let
完全取决于你的第一个例子,而不是。我也发现它更具可读性。
Rust是关于零成本抽象的,它使编程更好,if let
和while let
就是很好的例子(至少IMO - 我意识到这是个人偏好的问题)。它们不是绝对必要的,但它们确实使用起来很好(另见:Clojure,它们可能会被取消)。
答案 3 :(得分:0)
a related issue对 The Rust Programming Language 的引用:
您最好将
out = subprocess.check_output(["git", "remote","-v"]), stderr=subprocess.STDOUT)
视为单手if let
,而无需执行详尽的检查。它通常与match
或if
无关,这就是为什么它如此混乱的原因。 :)也许最好重命名整个运算符,将其命名为
let
或类似的名称。
(稍加修改即可在没有上下文的情况下变得有意义)