简体   繁体   English

如何根据与变体中的类型匹配的泛型类型选择枚举变体?

[英]How can I select an enum variant based on a generic type that matches a type inside the variant?

I have multiple types and an enum:我有多种类型和一个枚举:

struct Foo;
struct Bar;

enum MyEnum {
    Foos(Vec<Foo>),
    Bars(Vec<Bar>),
}

Is there a way to make a generic .push method for this, like有没有办法为此创建一个通用的.push方法,比如

impl MyEnum {
    fn push<T>(&mut self, item: T) {
        if item.type_id() == TypeId::of::<Foo>() {
            if let MyEnum::Foos(ref mut vec) = self {
                vec.push(item); // doesn't compile because item is generic type `T` instead of `Foo`
            }
        } else if item.type_id == TypeId::of::<Bar>() {
            if let MyEnum::Bars(ref mut vec) = self {
                vec.push(item);
            }
        }
    }
}

This doesn't compile because .push only sees generic type T , not the type it was expecting.这不会编译,因为.push只看到泛型类型T ,而不是它期望的类型。

Is there a way to accomplish this?有没有办法做到这一点?

Invert the logic — introduce a trait to select the appropriate enum variant based on the type, then call that trait from your method:反转逻辑 - 引入一个特征以根据类型选择适当的枚举变体,然后从您的方法中调用该特征:

trait Selector: Sized {
    fn select_mut<'a>(&self, e: &'a mut MyEnum) -> Option<&'a mut Vec<Self>>;
}

impl Selector for Foo {
    fn select_mut<'a>(&self, e: &'a mut MyEnum) -> Option<&'a mut Vec<Self>> {
        match e {
            MyEnum::Foos(vec) => Some(vec),
            _ => None,
        }
    }
}

impl Selector for Bar {
    fn select_mut<'a>(&self, e: &'a mut MyEnum) -> Option<&'a mut Vec<Self>> {
        match e {
            MyEnum::Bars(vec) => Some(vec),
            _ => None,
        }
    }
}

impl MyEnum {
    fn push<T: Selector>(&mut self, item: T) {
        if let Some(v) = item.select_mut(self) {
            v.push(item)
        }
    }
}

#[derive(Debug)]
struct Foo;
#[derive(Debug)]
struct Bar;

#[derive(Debug)]
enum MyEnum {
    Foos(Vec<Foo>),
    Bars(Vec<Bar>),
}

fn main() {
    let mut e = MyEnum::Foos(vec![]);
    e.push(Foo);
    e.push(Bar);
    // e.push(false); // the trait bound `bool: Selector` is not satisfied
    dbg!(e);
}

Note that this results in a compile-time error for types that cannot be stored in the enum.请注意,对于无法存储在枚举中的类型,这会导致编译时错误

For large enums, it might be worth creating a macro to reduce the duplication of the multiple Selector implementations.对于大型枚举,可能值得创建一个宏来减少多个Selector实现的重复。

See also:也可以看看:

This simplest way is to make an inner enum and accept that:这种最简单的方法是创建一个内部enum并接受:

enum MyEnumInner {
    Foo(Foo),
    Bar(Bar),
}

fn push_enum(&mut self, item: MyEnumInner) -> Result<()> {
    Ok(match (self, item) {
        (MyEnum::Foos(foos), MyEnumInner::Foo(foo)) => foos.push(foo),
        (MyEnum::Bars(bars), MyEnumInner::Bar(bar)) => bars.push(bar),
        _ => return Err(...),
    })
}

Then if we use the crate derive_more we can just as easily do the following:然后,如果我们使用 crate derive_more我们可以轻松地执行以下操作:

use derive_more::From;

#[derive(From)]
enum MyEnumInner {
    Foo(Foo),
    Bar(Bar),
}

fn push<T: Into<MyEnumInner>>(&mut self, item: T) -> Result<()> {
    Ok(match (self, item.into()) {
        (MyEnum::Foos(foos), MyEnumInner::Foo(foo)) => foos.push(foo),
        (MyEnum::Bars(bars), MyEnumInner::Bar(bar)) => bars.push(bar),
        _ => return Err(...),
    })
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM