Replacing values in Rust causing “cannot move out

2019-08-26 21:08发布

问题:

This question already has an answer here:

  • How can I swap in a new value for a field in a mutable reference to a structure? 2 answers

I'm trying to build a graphics engine that works with the fairly common two-buffer pattern. One buffer (current_buffer) is displayed while the next (next_buffer) is prepared, then the next buffer is moved into the current buffer, and is subsequently repopulated by a new buffer, repeating.

I know there are a lot of other questions about cannot move out of borrowed content errors, so I spent a while looking at all I can find so far before finally resorting to asking this as a question.

Here's my code:

pub fn update(&mut self, dt: f64) {
    if self.is_change_buffer {
        self.current_buffer = self.next_buffer;
        self.next_buffer = Buffer { ... }; // New buffer
    }
}

I'm running into problems with moving self.next_buffer to self.current_buffer, which is understandable considering that self.current_buffer = self.next_buffer would break the "one owner" principle. How do I tell the borrow checker that I'm not trying to alias self.next_buffer, I'm trying to move it, and put something new in its place?

A caveat due to a framework I'm working with is that the function signature of update() must be:

pub fn (&mut self, dt: f64) -> ()

Also, a Buffer cannot be cloned. Because of the way Buffers are stored, cloning them would be an extremely expensive operation, and so cannot practically be done at every screen refresh.

回答1:

How do I tell the borrow checker that I'm not trying to alias self.next_buffer, I'm trying to move it, and put something new in its place?

The borrow checker is not complaining about aliasing – it is complaining because you are moving a field out of a struct that you only borrowed, which is not allowed. However, moving out and immediately replacing with a new value is allowed, and there is a dedicated function for exactly this use case, called std::mem::replace(). With this function, your code becomes

self.current_buffer = mem::replace(&mut self.next_buffer, Buffer { ... });


回答2:

Probably the easiest solution is to use std::mem::swap:

std::mem::swap(&mut self.next_buffer, &mut self.current_buffer);
self.next_buffer = Buffer { ... }; // New buffer

You can also assign first and then swap, but I find it more natural this way.