Can I move out of a mutable reference as long as I promise to put some valid data back in before I use it again?

type Foo(u32);

impl Foo {
   fn some_mutation(self) -> Self {
      // pretend this is a more complicated mutation that can't be 
      // rewritten to take &mut self (and may possibly panic)
      Foo(self.0 + 1)
   }
   fn mutate_in_place(&mut self) {
      // Is there some way I can call self.some_mutation() here
      // and store the result back in self?
      // I'd be okay with using unsafe code here (i.e. cast the
      // reference to a pointer and manually call read() and write()).
      // Is it sound to do that, and if so, is there a helper function
      // that does it so I don't have to?
   }
}

You can move out of a &mut as long as you promise to immediately replace it with valid data (it's ok if its only purpose in life is to be a temporary placeholder), then put it back later and throw away the temporary placeholder data. For example, you can put some Default::default() data in there using std::mem::take(), or if you don't implement Default for whatever reason, you can construct a throwaway value of your own and use std::mem::replace() instead. If your type is Copy then you can operate on a temporary copy and write the result afterwards.

The exact thing you are looking for does not exist AFAIK (basically a fn replace_with<T, F: FnOnce(T) -> T>(dest: &mut T, f: F))

2 Likes

It does exist:

There are various flavors, all with caveats. The property that has to be guaranteed is that on function return or unwind due to panic, there will always be a valid Foo behind the &mut Foo. (One way to ensure this is "abort instead of unwinding".)

1 Like

Not without taking other steps to ensure an unitialized value is never observable. "Other steps" could be replacing with a default value, using Option<_> instead, or some careful use of unsafe and other measures (such as aborting on panic) to move the value out from underneath the &mut _. See this article.

The article mentions the take_mut crate; a more recent crate to handle this scenario is replace_with. It encapsulates the unsafe for the approaches that require it.

1 Like

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.