I'm having some trouble understanding how to use trait objects to store a value of different possible types, and later figuring out what concrete type was actually stored.
I have a struct Outer
that contains a generic inner
field:
#[derive(Debug)]
struct Outer<T: InnerTrait> {
inner: T,
}
trait InnerTrait: Any + Debug + 'static {}
impl InnerTrait for u32 {}
impl InnerTrait for u64 {}
So that this (obviously) works:
fn main() {
let a: Outer<u32> = Outer{inner: 32u32};
let b: Outer<u64> = Outer{inner: 64u64};
// error[E0308]: `if` and `else` have incompatible types
// let c = if true {
// Outer { inner: 32u32 }
// } else {
// Outer { inner: 64u64 }
// };
}
However, a
and b
have different types, so I couldn't store them in the same variable c
.
To solve this, I introduce a marker trait OuterTrait
so that I can store a Box<dyn OuterTrait>
:
impl<T: InnerTrait> OuterTrait for Outer<T> {}
fn main() {
let c: Box<dyn OuterTrait> = Box::new(Outer { inner: 23u32 });
let d: Box<dyn OuterTrait> = Box::new(Outer { inner: 46u64 });
let e: Box<dyn OuterTrait> = if true {
Box::new(Outer { inner: 32u32 })
} else {
Box::new(Outer { inner: 64u64 })
};
}
So this seems to work fine. Is this a reasonable approach? My initial try was to have e
be of type Box<Outer<dyn InnerTrait>>
, but that didn't work.
My issue then starts when I later want to figure out if e
was a Outer<u32>
or a Outer<u64>
. I got this to work on nightly
using the trait_upcasting
feature:
#![feature(trait_upcasting)]
fn main() {
let e: Box<dyn OuterTrait> = if true {
Box::new(Outer { inner: 32u32 })
} else {
Box::new(Outer { inner: 64u64 })
};
if let Ok(down) = (e as Box<dyn Any + 'static>).downcast::<Outer<u32>>() {
eprintln!("ok: {down:?}");
} else {
eprintln!("err: could not downcast to u32");
}
// prints: "ok: Outer { inner: 32 }"
}
But on stable
I get the error:
error[E0658]: cannot cast `dyn OuterTrait` to `dyn Any`, trait upcasting coercion is experimental
.
Is there a way of doing this on stable
? I feel like I'm doing something wrong, first upcasting to just downcast straight away...