【问题标题】:Is there a non-messy way to chain the results of functions that return Option values?有没有一种简单的方法来链接返回 Option 值的函数的结果?
【发布时间】:2015-07-01 22:01:10
【问题描述】:

我有一些看起来像这样的代码:

f(a).and_then(|b| {
    g(b).and_then(|c| {
        h(c).map(|d| {
            do_something_with(a, b, c, d)
        })
    })
})

其中fgh 返回Option 值。我需要在do_something_with 计算中使用所有中间值(abcd)。压痕非常深。有一个更好的方法吗?理想情况下,它看起来像这样(这当然行不通):

try {
    let b = f(a);
    let c = g(b);
    let d = h(c);
    do_something_with(a, b, c, d)
} rescue NonexistentValueException {
    None
}

【问题讨论】:

  • 所有函数都返回相同的选项类型吗?还是他们改变了类型?
  • 如果它们是:这是一个无宏的解决方案:is.gd/eItCTh 任何其他解决方案都需要值泛型或可变参数泛型或像 FixedSizeArray 这样的特性,仅用于元组

标签: rust nullable optional


【解决方案1】:

Rust 1.22

question mark operator 现在支持Option,因此您可以将函数编写为

fn do_something(a: i32) -> Option<i32> {
    let b = f(a)?;
    let c = g(b)?;
    let d = h(c)?;
    do_something_with(a, b, c, d) // wrap in Some(...) if this doesn't return an Option
}

Rust 1.0

Rust 标准库定义了一个 try! 宏(以及等价的 ? 运算符,从 Rust 1.13 开始)可以解决 Result 的这个问题。宏如下所示:

macro_rules! try {
    ($expr:expr) => (match $expr {
        $crate::result::Result::Ok(val) => val,
        $crate::result::Result::Err(err) => {
            return $crate::result::Result::Err($crate::convert::From::from(err))
        }
    })
}

如果参数是Err,它会从具有Err 值的函数返回。否则,它会评估为包裹在Ok 中的值。该宏只能在返回Result 的函数中使用,因为它会返回遇到的错误。

我们可以为Option做一个类似的宏:

macro_rules! try_opt {
    ($expr:expr) => (match $expr {
        ::std::option::Option::Some(val) => val,
        ::std::option::Option::None => return None
    })
}

然后你可以像这样使用这个宏:

fn do_something(a: i32) -> Option<i32> {
    let b = try_opt!(f(a));
    let c = try_opt!(g(b));
    let d = try_opt!(h(c));
    do_something_with(a, b, c, d) // wrap in Some(...) if this doesn't return an Option
}

【讨论】:

    【解决方案2】:

    try! for Result 概念的启发,让我们包装自己的宏,以便在 monad 降至 None 时提前从作用域返回。

    macro_rules! get(
        ($e:expr) => (match $e { Some(e) => e, None => return None })
    );
    

    (盗自this reddit thread

    现在你可以线性运行你的代码了:

    fn blah() -> Option<...> { // ... is the return type of do_something_with()
        let a = 123;
        let b = get!(f(a));
        let c = get!(g(b));
        let d = get!(h(c));
        do_something_with(a, b, c, d)
    }
    

    (runnable gist)

    【讨论】:

      猜你喜欢
      • 2017-05-20
      • 1970-01-01
      • 2016-10-05
      • 2019-07-21
      • 1970-01-01
      • 2011-04-12
      • 2020-10-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多