A pointer to a trait object, e.g. Box<dyn Component> is composed of two pointers:
a pointer to the instance of type T implementing the trait
a pointer to a vtable. For each method in Component and its supertraits, the vtable contains a function pointer to the actual implementation of the function
In your case, Propagate is a subtrait of Component therefore the vtable in Box<dyn Component> doesn't have any information about methods defined in Propagator even though your underlying type T implements it. You have multiple work-around:
Use the solution proposed by @Yandros in your last topic. Since Component is a subtrait of Propagator in his solution, a Box<dyn Component> will be able to call methods in the supertrait
Change the type of Box<dyn Component> to Box<dyn Propagator>
You can also coalesce two traits into a single trait, effectively merging the two vtables. This also works without supertraits relationship:
// &dyn PropagatorComponent can access methods
// defined in both Component and Propagator
trait PropagatorComponent: Component + Propagator {}
impl <T> PropagatorComponent for T where T: Component + Propagator {}
Note that in general you could also define a method in the supertrait to perform the conversion however you'd also have to make the Propagator trait public
pub trait Component: Sync {
// ...
fn as_propagator(&mut self) -> Option<&mut dyn Propagator>;
}
impl Component for NodeComponents {
fn as_propagator(&mut self) -> Option<&mut dyn Propagator> {
Some(self)
}
}
// you can now convert from &dyn Component to &dyn Propagator
// for i in self.children.iter_mut() {
// i.as_propagator().unwrap().propagate_update(engine, delta_time)
//}
Adding propagate_update in Component definitely works however you get back to the issue you had in your previous topic where you wanted to disallow library users to override propagate_update in Component
One warning here: dyn Component is a distinct type, separate from the concrete types it might dynamically dispatch to. If you’re using specialization to overwrite the blanket impl, the specialized version won’t apply through the dyn Component wrapper.
Specialization will let you override it for all dyn Component objects at once, but the only way to pierce the dyn veil and make a decision based on the type beyond a dynamic pointer is via the Any trait.
So if I have a NodeComponent implementing Component, and I then implement Propagator for NodeComponent (overriding), and then reffer to that NodeComponent as a dyn Component, and call the propagate_update on that dyn Component, will it call the default or the overriding method ?
If you refer to it as dyn Propagator, it will use whatever Propagator implementation would have been used at the point the cast to dyn Propagator was made:
&SomeSpecificComponent as &dyn Propagator => default impl
&NodeComponent as &dyn Propagator => overridden impl
&dyn Component as &dyn Propagator => default impl, or maybe a compile error
That is quite nice, I knew about the implicit Sized bound on type parameters and DST trait object type though I didn't realize that opting out of Sized in a blanket impl would also implement that trait for dyn Trait but this make sense.