Search code examples
rustoptional-chaining

Is there an idiomatic way of chaining Option in Rust?


When deserializing deeply nested structures (e.g. from JSON), it's not uncommon to have to traverse multiple Option types.

For example:

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

Is there an idiomatic way of chaining Option to access deeply nested values?

So far I have the following, but neither are as concise as foo.x?.y?.z in other languages that support optional-chaining:

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

It looks like the try_block feature might be a good fit, but it's currently unstable.

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

Solution

  • As you say, the try block would be perfect for this.

    In the meantime you can take advantage of the fact that ? works in functions, and wrap your expression in a closure and call it:

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

    You can write a simple macro to make it a bit nicer:

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

    Which works basically the same as the try block:

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