rust 在迭代器中找到第一个特定的枚举变量并对其进行转换

332nm8kg  于 2023-02-12  发布在  其他
关注(0)|答案(2)|浏览(131)

我有一个具有不同变体的枚举,我想找到第一个匹配的变体,然后通过返回变体值或将其Map到其他值来转换它。
在Scala中,我会使用case类来执行类似以下操作:

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

在Rust中,我必须进行两次模式匹配才能达到相同的结果。有没有办法让它不那么冗长?

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,
        });
}
yhqotfr8

yhqotfr81#

从Rust 1.30开始,可以使用Iterator::find_map

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

在此之前,您可以使用Iterator::filter_mapIterator::next

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

我真的不喜欢在迭代器链中使用大的match语句,所以我通常会在MyData上创建一个方法来代替:

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 { 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()
        .find_map(MyData::as_data_d_data)
        .map(str::to_owned);
}

事实上,我的很多枚举都有这样的模式:

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(x) => Some(x),
            _ => None,
        }
    }

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

    // Repeat for all variants
}

我甚至创建了一个板条箱来自动派生这些函数。我从来没有发表过它,因为我很确定类似的东西已经存在,只是我还没有找到它...

zkure5ic

zkure5ic2#

enum Foo {
    A(..)
    B(..)
}
impl Foo {
    fn equals(&self, foo: &Foo) -> bool {
        let eq = |foo: &Foo| match foo {
            A(_) => 0,
            B(_) => 1
        };
        eq(self) == eq(foo)
    }
}

相关问题