[英]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.