Search code examples
rustcollectionsenumsiterable

Use iteration to find a specific enum variant in a collection


Setup

I have an enum whose variants have struct parameters, along with a vector of the enum:

struct A { x: i64, };
struct B { x: f64, },
enum E {
   One(A),
   Two(B),
   // maybe others
};
fn main() {
   let v = vec![ One( A { x: 10 } ), Two( B { x : 5. } ), /* others as well */ ];
   // ...
}
Task

Find in v an element of type E::Two.

Question

What is a concise, Rust-idiomatic way to do this?

Preferred approach

Use the iterable find() method, without deriving or implementing Eq and PartialEq on A and B, since I want to find something of the same enum variant; I don't really care about the variant's contents. In addition, deriving Eq and PartialEq is overkill, and can get painful.


Solution

  • You can use the built-in matches! macro to make this easy:

    let found_item = v.iter().find(|i| matches!(i, E::Two(_)));
    

    Using the macro is a more concise way to write this equivalent code:

    let found_item = v.iter().find(|i| match i {
        E::Two(_) => true,
        _ => false,
    });
    

    In both cases, found_item will have type Option<&E>. If you only want to know if such an item is present, you can either invoke .is_some() on this result, or you can use Iterator::any(), which just returns a bool indicating if a matching element was found:

    let is_found = v.iter().any(|i| matches!(i, E::Two(_)));
    

    If you want to know the index of the found item, you can use Iterator::position():

    let index = v.iter().position(|i| matches!(i, E::Two(_)));
    

    index will be Option<usize>, which will only be Some if a matching item was found, and the contained usize value will be the index where the match occurred.