Last active
July 5, 2022 13:02
-
-
Save rbran/aab5861d75514e81b338b317f1a29db4 to your computer and use it in GitHub Desktop.
if you already have an Rc, downcast is cheap on Rust
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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