For converting a Markdown syntax tree to HTML, I need something more flexible than traits (I want various parties to be able to customize the rendering for any syntax node).
The main trick I used is defining a method in Renderable to get the dyn Any you need for downcasting. The second trait lets you provide a blanket implementation for all Sized types that will then be included in the vtable for dyn Renderable; without that, each implementor of Renderable would have to define as_any itself.
pub trait Renderable: AsAny {}
pub trait AsAny {
fn as_any(&self)->&dyn Any;
}
impl<T:Any> AsAny for T {
fn as_any(&self)->&dyn Any { self as &dyn Any }
}
I then removed some of the superfluous Boxing where you're really just passing references:
pub type RenderFunc = fn(&mut Renderer, &dyn Any);
// ...
impl Renderer {
pub fn render(&mut self, data: &(impl Renderable + ?Sized + 'static)) {
let key = data.as_any().type_id();
let func = self.render_funcs.get(&key).unwrap();
func(self, data.as_any());
}
}
// ...
mod tests {
#[test]
fn rendering() {
let mut renderer = Renderer::new();
fn render_chunks(renderer: &mut Renderer, chunks_any: &dyn Any) {
let chunks = chunks_any.downcast_ref::<Chunks>().unwrap();
for chunk in &chunks.chunks {
renderer.render(&**chunk);
}
()
}
renderer.set_render_func::<Chunks>(render_chunks);
// ...
}
}
In principle, I’m looking for extensible multiple dynamic dispatch. The create double-dynalmost gets me there but its multifunctions can’t be extended later. Thus, I’ll be using my code with the fixes proposed by @2e71828.