? just for a single expression?

What is the best way to have something like the ? for just a single expression instead of the whole function?

Consider some deeply nested data structure with lots of optional fields:

struct Bar {
    bar: Option<i32>,
    zoo: Option<Option<i32>>,
}
struct Foo {
    foo: Option<i32>,
    bar: Option<Bar>,
}

If I would want to extract a member deep down I could write a function:

fn get_me_that_zoo(x: &Foo, fallback: i32) -> i32 {
    x.bar?.zoo?.unwrap_or(fallback)
}

However writing those functions for every nested field is a bit cumbersome and doesn't feel quite right.

Is there an equivalent of ? but just for a single line so I could write something like this:

fn process(x: &Foo) {
    let q = value_or(x.bar?.zoo??, 42);
    if TARZO == 9 {
        println!("a {}", value_or(x.bar?.bar?, 43));
    } else {
        println!("bee {}", q / 2);
    }
}

Try blocks will let you do that, although you also need to be careful about moving the thing you unwrap, which is why I added as_ref() here:

#![feature(try_blocks)]

fn process(x: &Foo) {
    let q = value_or(try { x.bar.as_ref()?.zoo?? }, 42);
    if TARZO == 9 {
        println!("a {}", value_or(try { x.bar.as_ref()?.bar? }, 43));
    } else {
        println!("bee {}", q / 2);
    }
}

A stable way for now is with IIFE, like (|| x.bar.as_ref()?.zoo?)(). Note that I dropped the trailing ? to return it as-is, otherwise you would have to re-wrap it (which try blocks do implicitly).

3 Likes

This is great thank you!

Instead of an IIFE, you should use the .and_then() combinator on stable, it's less convoluted and it was designed for this purpose:

fn get_me_that_zoo(x: &Foo, fallback: i32) -> i32 {
    x.bar.as_ref().and_then(|b| b.zoo.flatten()).unwrap_or(fallback)
}

(Playground).

2 Likes

Right, that works, too! For a deep data structure ? just feels so much more succulent. Instead of a rather long winded one.and_then(|y| y.two).and_then(|y| y.three).four I can just write one?.two?.three?.four. I feels like a waste to allow ? for "return", but not for "single expression", but try {} sounds exactly like the solution to this issue.

For a deep data structure resulting in long chains, you should probably factor the code in question out into its own function anyway.