【问题标题】:Why does Rust need the `if let` syntax?为什么 Rust 需要 `if let` 语法?
【发布时间】:2015-02-16 12:27:44
【问题描述】:

来自其他函数式语言(并且是 Rust 新手),我对 Rust if let 语法的动机感到有些惊讶。 RFC 提到,如果没有 if let,“今天用于测试和展开 Option<T> 的惯用解决方案”要么是

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));

【问题讨论】:

  • 你如何在 Scala 中做这样的事情:if let Some(foo) = bar { ... } else if let Some(foo) = baz { ... } else if let Some(foo) = daz { ... } else { ... }?使用 map/flatMap 似乎非常嵌套:bar.map(...).orElse(baz.map(...).orElse(daz.map(...))).getOrElse(...)
  • @fantom:例如:List(bar, baz, daz).find(_.isDefined).foreach(println(_))
  • 是的,如果您需要在每个分支中执行相同的操作,但如果它们不同(而不是每个分支的 println)怎么办?我最近遇到了这个问题,没有发现比嵌套 map/orElse 更好的方法

标签: option rust


【解决方案1】:

因为您的解决方案创建了一个使用资源的闭包,而 if let 完全符合您的第一个示例,但没有。我也觉得它更具可读性。

Rust 是关于零成本抽象的,它使编程变得更好,if let 和 while let 就是很好的例子(至少在 IMO ——我意识到这是个人喜好问题)。它们不是绝对必要的,但使用起来确实感觉很好(另请参阅:Clojure,它们可能是从那里提取出来的)。

【讨论】:

  • 据我了解,它们是从Swift提取的
  • 所以换句话说:如果映射不是性能关键,就可以了吗?考虑到 if let 语法和 map 在语法上几乎相同,我想知道优化器是否真的可以避免首先创建闭包...
  • @bluenote10 使用新的未装箱闭包,map 实际上与使用 match 语句一样快,并且可能编译为完全相同的代码,因为闭包是静态调用的并且可以很容易内联。
  • 只是强调一下:.map() 也是一个零成本抽象。
【解决方案2】:

.map() 特定于 Option<T> 类型,但 if let(和 while let!)是适用于所有 Rust 类型的功能。

【讨论】:

  • 注意:我接受这个答案,因为它简要总结了主要的技术差异,但请参阅@Vladimir 的答案以获得更全面的解释。
【解决方案3】:

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()-like 方法; if let 仍然可以很好地与他们合作。所以if let 是一个明显的胜利,因此它是惯用的。

【讨论】:

  • 使用map 执行副作用只会让代码的读者感到困惑。 这就是为什么Scala 总是提供两个版本的map,一个用于转换,一个用于副作用。我喜欢 Scala 方法的地方在于它解决了相同的问题,而无需引入额外的语法。期待 Scala 用户在Options 上使用map 很多:)(它在 Scala 中无处不在)。
  • @bluenote10,我以编写 Scala 为生,所以我非常清楚这一点 :) 再次请注意,if let不仅适用于 Option,并且语法可以说比闭包更轻量级。
【解决方案4】:

引用 a related issue 反对 Rust 编程语言:

您最好将if let 视为单手操作的match,没有强制执行详尽的检查。它通常与if 或let 无关,这就是它如此令人困惑的原因。 :)

也许最好重命名整个运算符,将其命名为match once 或类似的名称。

(稍作修改以在没有上下文的情况下有意义)

【讨论】:

  • if let 也令人困惑,因为右值位于左值的左侧。
猜你喜欢
  • 2021-03-21
  • 1970-01-01
  • 1970-01-01
  • 2012-03-08
  • 2012-09-10
  • 1970-01-01
  • 1970-01-01
  • 2011-04-21
  • 1970-01-01
相关资源
最近更新 更多