简体   繁体   中英

How to get mutable struct from boxed trait

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

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 variable 1 .

The code has:

  • An immutable variable
  • An immutable reference to that variable
  • Calls Any::downcast_ref , which returns an immutable reference

When 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:

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM