Skip to content

Instantly share code, notes, and snippets.

@rbran
Last active July 5, 2022 13:02
Show Gist options
  • Save rbran/aab5861d75514e81b338b317f1a29db4 to your computer and use it in GitHub Desktop.
Save rbran/aab5861d75514e81b338b317f1a29db4 to your computer and use it in GitHub Desktop.
if you already have an Rc, downcast is cheap on Rust
use std::any::Any;
use std::collections::HashMap;
use std::rc::Rc;
struct Foo {
id: u8,
name: String,
value: u128,
}
struct Bar {
id: u32,
name: &'static str,
}
trait FooBar: Any {
fn id(&self) -> u64;
fn name(&self) -> &str;
//create and return the FooBar Any trait call table
fn as_any(&self) -> &dyn Any;
}
impl FooBar for Foo {
fn id(&self) -> u64 {
self.id.into()
}
fn name(&self) -> &str {
&self.name
}
fn as_any(&self) -> &dyn Any {
self
}
}
impl FooBar for Bar {
fn id(&self) -> u64 {
self.id.into()
}
fn name(&self) -> &str {
self.name
}
fn as_any(&self) -> &dyn Any {
self
}
}
fn main() {
let all_foo = [
Rc::new(Foo {
id: 0,
name: "foo0".to_string(),
value: 1,
}),
Rc::new(Foo {
id: 1,
name: "foo1".to_string(),
value: 4,
}),
];
let all_bar = [
Rc::new(Bar {
id: 2,
name: "bar0",
}),
Rc::new(Bar {
id: 3,
name: "bar1",
}),
];
let mut by_id: HashMap<u64, Rc<dyn FooBar>> = HashMap::new();
let mut by_name: HashMap<String, Rc<dyn FooBar>> = HashMap::new();
//the closure forces the converstion from foo/bar into dyn FooBar
//without any runtime cost compared to a normal Rc::clone
let foo_iter = all_foo.iter().map(|x| -> Rc<dyn FooBar> { x.clone() });
let bar_iter = all_bar.iter().map(|x| -> Rc<dyn FooBar> { x.clone() });
for foo_bar in foo_iter.chain(bar_iter) {
by_id.insert(foo_bar.id(), Rc::clone(&foo_bar));
by_name.insert(foo_bar.name().to_string(), foo_bar);
}
fn print_foo_bar(foo_bar: &dyn FooBar) {
println!("id: {}", foo_bar.id());
println!("name: {}", foo_bar.name());
//the Any table can be stored on the stack, so there is minimum overhead
let foo_bar = foo_bar.as_any();
if let Some(foo) = foo_bar.downcast_ref::<Foo>() {
println!("subtype: Foo value: {}", foo.value);
} else if foo_bar.is::<Bar>() {
println!("subtype: Bar");
} else {
unreachable!()
}
}
let id_0 = by_id.get(&0).unwrap();
println!("FooBar with id 0");
print_foo_bar(id_0.as_ref());
let name_foo0 = by_name.get("bar0").unwrap();
println!("\nFooBar with name bar0");
print_foo_bar(name_foo0.as_ref());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment