I have the following case where there's a non-object-safe trait SystemData
coming from an external crate, and I would like to create a trait object that can call one of it's functions. I've created an intermediate trait that is object safe and implemented it for PhantomData<T>
where T
implements SystemData
. There's a second trait PrefabData
that has an associated type bound on SystemData
. I'd like to be able to take a type that implements PrefabData
and then create a trait object out of its associated type.
The tricky part is that both SystemData
and PrefabData
have a generic lifetime parameter. In most cases I'm able to use HRTB when writing trait implementations or functions to get this working (see example below). However, I've found that the compiler can't seem to follow an HRBT through an associated type.
Is there a way to write the trait bounds for T
in the register_component<T>
function such that the below code will compile? And if not, why? It seems to me like the below code should be sufficient, though my understanding of HRTBs is pretty weak (this is the first time I've ever used them).
For slightly more context, here's a more complete example that includes some extra traits that do compile. Note that all the ones that do compile apply the bounds to the trait directly and don't need the bounds to be threaded through associated types.
I appreciate any help/guidance anyone can provide!
use std::marker::PhantomData;
// >>> BELOW CODE IS FROM AN EXTERNAL CRATE AND CANNOT BE CHANGED <<< //
pub struct Resources;
pub trait SystemData<'a> {
fn setup(res: &Resources);
}
pub trait PrefabData<'a> {
type SystemData: SystemData<'a>;
}
// >>> BELOW CODE IS TRAIT DEFINITIONS AND LIKELY SHOULD NOT BE CHANGED <<< //
pub trait SystemDataSetup {
fn setup(&self, res: &mut Resources);
}
impl<T> SystemDataSetup for PhantomData<T>
where
for<'a> T: SystemData<'a>,
{
fn setup(&self, res: &mut Resources) {
T::setup(res);
}
}
// >>> BELOW CODE CAN BE CHANGED <<< //
pub fn register_component<T>()
where
T: 'static,
for<'a> T:
PrefabData<'a>,
{
let _ = Box::new(PhantomData::<T::SystemData>) as Box<dyn SystemDataSetup>;
}
fn main() {}
Errors:
Compiling playground v0.0.1 (/playground)
error[E0277]: the trait bound `for<'a> <T as PrefabData<'_>>::SystemData: SystemData<'a>` is not satisfied
--> src/main.rs:39:13
|
39 | let _ = Box::new(PhantomData::<T::SystemData>) as Box<dyn SystemDataSetup>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'a> SystemData<'a>` is not implemented for `<T as PrefabData<'_>>::SystemData`
|
= help: consider adding a `where for<'a> <T as PrefabData<'_>>::SystemData: SystemData<'a>` bound
= note: required because of the requirements on the impl of `SystemDataSetup` for `std::marker::PhantomData<<T as PrefabData<'_>>::SystemData>`
= note: required for the cast to the object type `dyn SystemDataSetup`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.
error: Could not compile `playground`.
To learn more, run the command again with --verbose.