在Rust中有没有一种惯用的方法来链接Option?

mtb9vblg  于 2023-03-02  发布在  其他
关注(0)|答案(3)|浏览(177)

当反序列化深度嵌套的结构(例如,从JSON)时,必须遍历多个Option类型的情况并不少见。
例如:

let foo = Foo {
    x: Some(Bar {
        y: Some(Baz {
            z: Some(42),
        })
    })
};

有没有一种惯用的方法来链接Option以访问深度嵌套的值?
到目前为止,我已经有了以下代码,但它们都不如其他支持可选链接的语言中的foo.x?.y?.z那样简洁:

let z = foo.x.as_ref().and_then(|x| x.y.as_ref()).and_then(|y| y.z);
let z = foo.x.as_ref().and_then(|x| x.y.as_ref()?.z);
let z = (|| foo.x.as_ref()?.y.as_ref()?.z)();

看起来try_block特性可能是一个很好的选择,但是它目前还不稳定。

let z = try { foo.x.as_ref()?.y.as_ref()?.z };
0s0u357o

0s0u357o1#

正如您所说,try块非常适合于此。
与此同时,你可以利用?在函数中工作的事实,将你的表达式 Package 在闭包中并调用它:

let z = (|| foo.x.as_ref()?.y.as_ref()?.z )();

你可以写一个简单的宏来使它更好一些:

macro_rules! tryit {
    ($($e: tt)+) => {
        (|| { $($e)+ })()
    }
}

它的工作原理与try块基本相同:

let z = tryit! { foo.x.as_ref()?.y.as_ref()?.z };
qnakjoqk

qnakjoqk2#

如果你不喜欢立即调用闭包,另一个选择是使用类似map_formdo的crate。

use map_for::option::FlatMap;
let z = map_for!{
    x <- foo.x.as_ref();
    y <- x.y.as_ref();
    => y.z }:

免责声明:我写的map_for板条箱。

ekqde3dh

ekqde3dh3#

另一种选择是对if let使用重构:

struct Foo {
    x: Option<Bar>,
}
struct Bar {
    y: Option<Baz>,
}
struct Baz {
    z: Option<i32>,
}

fn main() {
    let foo = Foo {
        x: Some(Bar {
            y: Some(Baz { z: Some(42) }),
        }),
    };

    if let Foo {
        x: Some(Bar {
            y: Some(Baz { z: Some(num) }),
        }),
    } = &foo
    {
        println!("contains number: {}", num);
    }
}
contains number: 42

相关问题