是否有一种非混乱的方式来链接返回Option值的函数的结果?

Adr*_*ian 8 nullable optional rust

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

f(a).and_then(|b| {
    g(b).and_then(|c| {
        h(c).map(|d| {
            do_something_with(a, b, c, d)
        })
    })
})
Run Code Online (Sandbox Code Playgroud)

Where f,gh返回Option值.我需要使用所有的中间值(a,b,c,和d在)do_something_with计算.压痕非常深.有一个更好的方法吗?理想情况下它看起来像这样(当然这不起作用):

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

Fra*_*gné 8

Rust 1.22

问号运营商现在支持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
}
Run Code Online (Sandbox Code Playgroud)

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))
        }
    })
}
Run Code Online (Sandbox Code Playgroud)

如果参数是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
    })
}
Run Code Online (Sandbox Code Playgroud)

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

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
}
Run Code Online (Sandbox Code Playgroud)


mdu*_*dup 6

灵感来自try!于Result 的概念,如果monad降为None,让我们将自己的宏包装到范围的早期返回.

macro_rules! get(
    ($e:expr) => (match $e { Some(e) => e, None => return None })
);
Run Code Online (Sandbox Code Playgroud)

(从这个reddit线程中偷来的)

现在,您可以线性运行代码:

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)
}
Run Code Online (Sandbox Code Playgroud)

(可运行的要点)