I am very new to Rust, so apologies if this is a simple question!
I have the following simplified scenario, where I have a trait and two struct types (TypeA and TypeB) that implement the trait. I then have a vector of traits that contains instances of both TypeA and TypeB.
Later on, I would like to iterate the vector and access fields that belong only to TypeA or TypeB depending on what the type is. Below is what I have written. My attempt is to match the type of each item and then downcast the boxed trait iterator to the struct. This works with a &
reference, but how can I achieve a mutable reference &mut
.
For the &
reference consider the following ...
use std::any::Any;
trait MyTrait {
fn do_something(&self);
fn as_any(&self) -> &dyn Any;
}
struct TypeA{field_a: u16}
impl MyTrait for TypeA {
fn do_something(&self) {
println!("TypeA is doing something.");
}
fn as_any(&self) -> &dyn Any {
self
}
}
struct TypeB{field_b: u16}
impl MyTrait for TypeB {
fn do_something(&self) {
println!("TypeB is doing something.");
}
fn as_any(&self) -> &dyn Any {
self
}
}
fn main() {
let vec: Vec<Box<dyn MyTrait>> = vec![
Box::new(TypeA{field_a:10}),
Box::new(TypeB{field_b:20}),
];
for item in &vec {
// Downcast the trait object to its concrete type
if let Some(type_a) = item.as_any().downcast_ref::<TypeA>() {
type_a.do_something();
print!("{}", type_a.field_a);
} else if let Some(type_b) = item.as_any().downcast_ref::<TypeB>() {
type_b.do_something();
print!("{}", type_b.field_b);
} else {
println!("Unknown type");
}
}
}
However, I want to modify the fields within the downcasted instance, such as
type_a.field_a = 100;
You can do this if you add a way to get a &mut dyn Any
on your trait:
fn as_any_mut(&mut self) -> &mut dyn Any;
The implementation is the same as as_any
:
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
You will also need to make vec
mutable, and iterate over &mut vec
instead of &vec
, and then you will be able to do this, for example:
item.as_any_mut().downcast_mut::<TypeA>()
This gives you an Option<&mut TypeA>
.
However, I would strongly suggest you consider using an enum instead of boxed trait objects. If you have a closed set of variants you need to support, this will be considerably more ergonomic.