Idiomatic way to filter a list of options to all the Some values and return None if all values are None

I have a list of Options. I want to unwrap all the Some in the list and if everything is a None, return a None. Is there an idiomatic way to do this.

filter_map is close to what I want but if all elements of my list are None it returns an empty list. So my code currently uses two lines. Is there a better way of doing this?

fn main() {
    let v : Vec<Option<i32>> = vec![None, None, None, None];
    println!("{:?}",&v); // [None, None, None, None]
    let w : Vec<_> = v.iter().filter_map(|x| x.as_ref()).collect();
    println!("{:?}",&w); // []
    let z = if w.len() == 0 {None} else {Some(w)};
    println!("{:?}",&z); // None
}

I don't think so, because what you want and what you get are two different types. Why do you want the whole thing to become None if there are no elements? It sounds like you have a check further along in the code that wants to check for None; could you change that to is_empty()?

4 Likes

This is fine. You don't have to force all code to be a single expression or line.

2 Likes

Thanks. is_empty() is indeed more elegant.

DELETED: the delusions of a madman posting past his bedtime. :slight_smile:

1 Like

That's incorrect: it would return a None if any of the values are None. OP needs to collect all Some variants if there are any, and only get a None if all values were None.

1 Like

To expand on @chjordan's good answer:

The original request has the fundamental issue that it results in a "shouldn't happen" value that's not actually prevented in the type system: Some(vec![]).

So it's nicer to not force the downstream code to deal with that. Unless you really need to distinguish between None and empty, it's better to just not have the Option wrapper, and represent that state with an empty collection.

That's especially true for things like Vec and String, since they don't allocate for empty collections. And thus there's no value in avoiding creating them, the way there can be in things like C# where allocating the collection has cost even if it's empty.

(For integer types there's the possibility to choose between u32 and Option<NonZeroU32>, depending which better represents the domain, but there's no NonEmptyVec<_> that you can use here. And it's probably not worth making one.)

5 Likes