Search code examples
referencecopyrustmovemutable

How to update a mutable reference to a noncopyable type?


I want to do something like

impl Widget {
    fn foo(self, rhs: Widget) -> Self {
        // Some stuff
    }

    fn update(&mut self, rhs: Widget) {
        *self = (*self).foo(rhs)
    }
}

but the compiler complains "cannot move out of borrowed context." What's the right way to do this?


Solution

  • One option is to use the take_mut crate, which offers the take functions that do exactly what you want:

    take allows for taking T out of a &mut T, doing anything with it including consuming it, and producing another T to put back in the &mut T.

    As pointed by Francis Gagné, the problem of doing this is that the &mut reference will be in a invalid state if a panic happens, and this can lead to undefined behavior. The take_mut approach is:

    During take, if a panic occurs, the entire process will be exited, as there's no valid T to put back into the &mut T.

    Here is the code using take:

    extern crate take_mut;
    
    struct Widget;
    
    impl Widget {
        fn foo(self, rhs: Widget) -> Self {
            self
        }
    
        fn update(&mut self, rhs: Widget) {
            take_mut::take(self, |s| s.foo(rhs));
        }
    }