简体   繁体   中英

Generic downcast of enum variant

Suppose the following trait and enum

#[enum_dispatch(Components)]
trait Component {}
struct A;
struct B;
impl Component for A {}
impl Component for B {}

#[enum_dispatch]
enum Components {
    A,
    B,
}

Now I want to be able to "downcast" an instance of my enum in a generic way, ie I want to write

let foo = Components::A(A);
dbg!(foo.downcast::<A>()); // Some(&A) : Option<&A>
dbg!(foo.downcast::<B>()); // None : Option<&A>

Is this possible at all?

Background: I am trying something akin to but not quite like an ECS and trait objects are a bad fit for various reasons, thus I ended up with enum_dispatch . I still need my components in a HashMap though but need to access the inner values with a known type. I also really wish for the type to be a parameter and not part of a function name (ie something like foo.downcast_A() ) because I'd like to have higher order functions on top of that:

fn apply<C, F>(target: &Components, f: F) 
    where C: Components, F: FnOnce(C)
{
    if let Some(x) = target.downcast::<C>() {
        f(x);
    }
}

I was able to achieve something similar with macro_rules! but carrying an identifier instead of a typename through a callchain is not very ergonomic.

After some tinkering I came up with this:

use std::any::TypeId;
use enum_dispatch::enum_dispatch;

#[enum_dispatch(Components)]
trait Component: 'static {
    fn downcast<T: 'static>(&self) -> Option<&T> {
        if TypeId::of::<T>() == TypeId::of::<Self>() {
            Some(unsafe { std::mem::transmute(self as *const Self as *const u8) })
        }  else {
            None
        }
    }
}

#[derive(Debug)]
struct A;
impl Component for A {}

#[derive(Debug)]
struct B;
impl Component for B {}

#[enum_dispatch]
#[derive(Debug)]
enum Components {
    A,
    B,
}

fn main() {
    let foo = Components::A(A);
    dbg!(foo.downcast::<A>()); // Some(A)
    dbg!(foo.downcast::<B>()); // None
}

This works but is arguably ugly. Also I don't know if the 'static requirement on the trait will come back to be a problem. Better solutions are very welcome!

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.

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