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
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.
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.
See also:
This simplest way is to make an inner enum
and accept that:
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:
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(...),
})
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.