5

If I have a number of Option<T>'s and I want to pick the first one that is Some rather than None - is there an idiomatic way to do that?

My naive approach:

pub fn pick_first_option_available<T>(a: Option<T>, b: Option<T>, c: Option<T>) -> Option<T> {
    match a {
        Some(a) => Some(a),
        None => match b {
            Some(b) => Some(b),
            None => match c {
                Some(c) => Some(c),
                None => None,
            },
        },
    }
}

One obvious issue with the above is that it's limited to a fixed number of Options (3). I would prefer to have a more general function.

There is a somewhat related thread here, but it tackles summing instead of picking options.

Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
ilmoi
  • 1,994
  • 2
  • 21
  • 45

2 Answers2

13

Yes, there's a simple and idiomatic way:

a.or(b).or(c)

You don't usually define a function just for this.

Option#or

Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
0

As @DenysSeguret answer and you could even abstract it for a generic number of options:

use std::array;


pub fn pick_first_option_available<T, const N: usize>(options: [Option<T>; N]) -> Option<T> {
    array::IntoIter::new(options).reduce(Option::or).flatten()
}

Playground

Also, looks like unpopular but still a way of doing it other than Option::or with a simple loop:

use std::array;

pub fn pick_first_option_available<T>(a: Option<T>, b: Option<T>, c: Option<T>) -> Option<T> {
    for option in array::IntoIter::new([a, b, c]){
        if option.is_some() {
            return option;
        }
    }
    None
}

Playground

Netwave
  • 40,134
  • 6
  • 50
  • 93
  • I wonder if the first variant will implement the short-circuiting one (presumably) wants. Maybe something along the lines of `iter.skip_while(Option::is_none).map(Option::unwrap).next()` would have the same effect, and stop iterating at first Some. If you don't like the unwrap, you could use `scan`: `iter.scan(true, |proceed, opt| proceed.then(|| { *proceed = false; opt })).flatten().next()` but that is getting quite obscure. – user4815162342 Jun 04 '21 at 17:46
  • Definitely not short circuiting @user4815162342. It may be solved as you say. – Netwave Jun 04 '21 at 21:28