? 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.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.