Reputation: 59
I'd like to modify data in a struct based on a trait that is boxed. The following code prints the value but gives me "cannot mutably borrow immutable field" when I try to change it or "cannot borrow as mutable" when calling its function.
My plan is to have a vector of Ai
each containing the AiData
derived struct and then iterate over them, set some data in it and call the tick()
function.
use std::any::Any;
pub trait AiData {
fn tick(&mut self);
fn as_any(&self) -> &Any;
}
pub struct Ai {
pub ai_data: Box<AiData>,
}
impl Ai {
pub fn new(ai_data: Box<AiData>) -> Ai {
Ai { ai_data: ai_data }
}
}
pub struct TestAi {
pub index: u8,
}
impl TestAi {
pub fn new() -> TestAi {
TestAi { index: 1 }
}
}
impl AiData for TestAi {
fn tick(&mut self) {
println!("tick");
}
fn as_any(&self) -> &Any {
self
}
}
fn main() {
let ai_data: TestAi = TestAi::new();
let ai: Ai = Ai::new(Box::new(ai_data));
let b: &TestAi = match ai.ai_data.as_any().downcast_ref::<TestAi>() {
Some(b) => b,
None => panic!("&a isn't a B!"),
};
println!("{:?}", b.index);
b.tick();
b.index = 2;
}
error[E0596]: cannot borrow immutable borrowed content `*b` as mutable
--> src/main.rs:48:5
|
48 | b.tick();
| ^ cannot borrow as mutable
error[E0594]: cannot assign to immutable field `b.index`
--> src/main.rs:49:5
|
49 | b.index = 2;
| ^^^^^^^^^^^ cannot mutably borrow immutable field
Upvotes: 0
Views: 8296
Reputation: 430300
How to get mutable struct from boxed trait
You cannot get a struct from the boxed trait object. You can get a reference to the struct, however.
As explained in The Rust Programming Language's chapter on variables and mutability, mutability is a property of the binding. Additionally, as described in the chapter on references and borrowing, a mutable reference (&mut T
) is distinct from an immutable reference (&T
). Based on these two points, you cannot get a mutable reference from an immutable variable1.
The code has:
Any::downcast_ref
, which returns an immutable referenceWhen you fix all of those, the code works:
use std::any::Any;
pub trait AiData {
fn tick(&mut self);
fn as_any_mut(&mut self) -> &mut Any;
}
pub struct Ai {
pub ai_data: Box<AiData>,
}
impl Ai {
pub fn new(ai_data: Box<AiData>) -> Ai {
Ai { ai_data }
}
}
pub struct TestAi {
pub index: u8,
}
impl TestAi {
pub fn new() -> TestAi {
TestAi { index: 1 }
}
}
impl AiData for TestAi {
fn tick(&mut self) {
println!("tick");
}
fn as_any_mut(&mut self) -> &mut Any {
self
}
}
fn main() {
let ai_data = TestAi::new();
let mut ai = Ai::new(Box::new(ai_data));
let b = ai.ai_data
.as_any_mut()
.downcast_mut::<TestAi>()
.expect("&a isn't a B!");
println!("{:?}", b.index);
b.tick();
b.index = 2;
}
1 You can read about interior mutability which actually does allow you to get a mutable reference from an immutable variable, at the expense of introducing runtime checks to prevent aliasing.
See also:
Upvotes: 2