Mutable reference to dynamic Trait

Hello,

I am trying to hold a dynamic trait option in a sub object, but I am getting the error message:

error[E0596]: cannot borrow `**handler` as mutable, as it is behind a `&` reference
  --> src/main.rs:22:17
   |
22 |                 handler.some_function();
   |                 ^^^^^^^ `handler` is a `&` reference, so the data it refers to cannot be borrowed as mutable

The reason for holding a reference to a dynamic trait is, that in my crate I know only the Trait, but not the actual object I get. Here I just simpliefied it.
Does anybody know how to get around it?

trait Trait {
    fn some_function(&mut self);
}

struct Test {}

impl Trait for Test {
    fn some_function(&mut self) {
        // do something which requires mut self
        println!("Test");
    }
}

struct Reader<'a> {
    pub handler: &'a Option<&'static mut dyn Trait>,
}

impl<'a> Reader<'a> {
    fn read(&mut self) {
        match self.handler {
            None => {}
            Some(handler) => {
                handler.some_function();
            }
        }
    }
}

struct Object {
    pub handler: Option<&'static mut dyn Trait>,
}

impl Object {
    fn init(&mut self, h: &'static mut Test) {
        self.handler = Some(h);
    }

    fn do_something(&mut self) {
        let mut r = Reader {
            handler: &self.handler,
        };

        // Reader is doing something
    }
}

fn main() {
    static mut h: Test = Test {};

    let mut obj = Object { handler: None };
    unsafe {
        obj.init(&mut h);
    }
    obj.do_something();
}

Your example code does not compile for reasons other than the mutability error. Could you provide a link to play.rust-lang.org demonstrating your issue?

Also, you need to know a lot about safety to be able to write correct code that uses unsafe to obtain a mutable reference to a static like that. Consider using lazy_static and a lock (Mutex/RwLock).

@mickvangelderen thanks for the fast reply. I created the demonstration:

Strange that it does not compile for you. I forgot to mention that the crate is created without std.

struct Reader<'a> {
-    pub handler: &'a Option<&'static mut dyn Trait>,
+    pub handler: &'a mut Option<&'static mut dyn Trait>,
}

Rust Playground

You can't mutate things through shared references, i.e. &&mut will never act like &mut. Simplified code with the same problem as follows

fn main() {
    let mut s = String::new();
    
    // fail
    let handler = &Some(&mut s);
    match handler {
        Some(var) => var.push('.'), // var: &&mut std::string::String
        None => (),
    };
    
    // ok
    let handler = &mut Some(&mut s);
    match handler {
        Some(var) => var.push('.'), // var: &mut &mut std::string::String
        None => (),
    };
}

error[E0596]: cannot borrow `**var` as mutable, as it is behind a `&` reference
 --> src/main.rs:7:22
  |
7 |         Some(var) => var.push('.'), // var: &&mut std::string::String
  |                      ^^^ `var` is a `&` reference, so the data it refers to cannot be borrowed as mutable

Thank you @vague . This solved the problem.

I have seen that in this case I could also let the Option in Reader away.

trait Trait {
    fn some_function(&mut self);
}

struct Test {}

impl Trait for Test {
    fn some_function(&mut self) {
        // do something which requires mut self
        println!("Test");
    }
}

struct Reader {
    pub handler: &'static mut dyn Trait,
}

impl Reader {
    fn read(&mut self) {
        self.handler.some_function();
    }
}

struct Object {
    pub handler: Option<&'static mut dyn Trait>,
}

impl Object {
    fn init(&mut self, h: &'static mut Test) {
        self.handler = Some(h);
    }

    fn do_something(&mut self) {
        let mut r = Reader {
            handler: self.handler.unwrap(),
        };
        // Reader is doing something
    }
}

fn main() {
    static mut h: Test = Test {};

    let mut obj = Object { handler: None };
    unsafe {
        obj.init(&mut h);
    }
    obj.do_something();
}

But this does not work as this. Sorry for my beginner questions

By the way, static mut is highly dangerous. Are you sure you need it? There are usually alternatives that are much easier to use correctly (and may not require any unsafe at all); in particular, if you just need a &'static mut reference to a single value, then Box::leak() is a good way to do that, and if you aren't certain that 'static lifetime is necessary here, then there's probably a way to avoid it — for example, it may be that you're using a &'static mut dyn Trait where a Box<dyn Trait> would be more fitting.

But in this case I would need std again. I have seen that heapless has also a Box object, but I have to look into it.

once_cell::race can be used with no_std.

1 Like

@Cerber-Ursi Thanks I will check it out

Sorry, I missed that you said this was a no_std application. Still, it is possible that you don't need 'static lifetime and can have Object take a borrow from a let mut in main() instead of a static mut.

Yes this might be possible. I will try it out. Thank you!

I implemented Object now as:

struct Object<T> {
    pub handler: T,
}

So I don't even need a variable outside of my object. In this case the Object can be in another crate

The implementation:

impl<T> Object<T>  where T: Trait {
   pub fn new(handler: T) {
       Object {
            handler,
       }
   }
}

In this case I don't need the Option and no dyn Trait.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.