You can get away without specialization
, by using an unnameable type trick:
impl<T : Component> PropagateUpdate for T {
fn propagate_update(&mut self, engine: &mut Engine, delta_time: f64)
{
self.__propagate_update_impl__(engine, delta_time, Private)
}
}
pub trait Component: Send + PropagateUpdate {
fn new() -> Self where Self: Sized;
fn init(&mut self, engine: &mut Engine) {}
fn update(&mut self, engine: &mut Engine, delta_time: f64) {}
#[cfg(feature = "docs")]
/// **This method cannot be overriden.**
fn propagate_update(&mut self, engine: &mut Engine, delta_time: f64) {
unreachable!("For docs only!");
}
#[doc(hidden)]
fn __propagate_update_impl__ (
self: &'_ mut Self,
engine: &'_ mut Engine,
delta_time: f64,
_: Private, // <- this is what makes the whole function unimplementable for downstream users
)
{
self.update(engine, delta_time);
}
}
pub(in crate) use helpers::{Private, __ as PropagateUpdate};
mod helpers {
pub struct Private;
pub trait __ { // use this weird name to give it "less visibility" in the docs
fn propagate_update(&mut self, engine: &mut Engine, delta_time: f64);
}
}
Advantages of this approach:
-
by having
Component
be the subtrait rather than a super trait, people can call.propagate_update(...)
simply byuse
ingComponent
. -
Some
doc
-related tweaks to help readability (real world example). -
If you still want to provide overrides within your crate, the
__propagate_update_impl__
escape hatch is available:impl Component for NodeComponent { ... fn __propagate_update_impl__ ( self: &'_ mut Self, engine: &'_ mut Engine, delta_time: f64, _: Private, ) { self.update(engine, delta_time); for child in &mut self.children { child.propagate_update(engine, delta_time); } } }