Find the first specific enumeration option in the iterator and convert it

I have an enumeration with various options, and I want to find the first option that matches, then converts it, returning the value of the option or matching it with something else.

In Scala, I would use case classes to do something like:

data.collectFirst{ case d: DataD => d.data }

In Rust, I need to match a pattern twice to achieve the same result. Is there any way to make it less verbose?

enum MyData {
    DataA(String),
    DataB(u64),
    DataC(bool),
    DataD { data: String, val: u32 },
}

fn main() {
    // test data
    let data = vec![
        MyData::DataB(42),
        MyData::DataD {
            data: "meaning of life".to_owned(),
            val: 42,
        },
        MyData::DataC(false),
    ];

    // find first one that matches and map it
    let found: Option<String> = data.iter()
        .find(|d| match **d {
            MyData::DataD { .. } => true,
            _ => false,
        })
        .and_then(|d| match *d {
            MyData::DataD { ref data, .. } => Some(data.to_owned()),
            _ => None,
        });
}
+4
source share
1 answer

You can use Iterator::filter_map:

let found: Option<String> = data.iter()
    .filter_map(|d| {
        match *d {
            MyData::DataD { ref data, .. } => Some(data.to_owned()),
            _ => None,
        }
    })
    .next();

I don't really like having big statements matchin my iterator chains, so I would usually use a method MyDatato use:

enum MyData {
    DataA(String),
    DataB(u64),
    DataC(bool),
    DataD { data: String, val: u32 },
}

impl MyData {
    fn as_data_d_data(&self) -> Option<&str> {
        match *self {
            MyData::DataD { ref data, .. } => Some(data),
            _ => None,
        }
    }
}

fn main() {
    let data = vec![
        MyData::DataB(42),
        MyData::DataD {
            data: "meaning of life".to_owned(),
            val: 42,
        },
        MyData::DataC(false),
    ];

    let found: Option<String> = data.iter()
        .filter_map(MyData::as_data_d_data)
        .map(str::to_owned)
        .next();
}

, :

enum MyData {
    DataA(String),
    DataB(u64),
    DataC(bool),
    DataD(D), // made into a struct
}

impl MyData {
    fn is_a(&self) -> bool {
        match *self {
            MyData::DataA(..) => true,
            _ => false,
        }
    }

    fn as_a(&self) -> Option<&String> {
        match *self {
            MyData::DataA(ref x) => Some(x),
            _ => None,
        }
    }

    fn into_a(self) -> Option<String> {
        match self {
            MyData::DataA(x) => Some(x),
            _ => None,
        }
    }

    // Repeat for all variants
}

. , , , ...

+5

Source: https://habr.com/ru/post/1694486/


All Articles