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.