"Cannot move out of borrowed content", why?


#1

Could someone explain why the following snippet does not work? It is not intuitive for me.


struct X;

struct Y {
    x: X
}

impl Y {
    pub fn foo(&mut self) {
        self.x = self.x;
    }
}

The error is “cannot move out of borrowed content” on self.x = self.x. I’d understand if I was assigning it to something else.

I am aware that the function is not meaningful, but it is a minimized example that I do not understand.

Thanks.


#2

I’m guessing the compiler stops when it detects a move out of a borrow, and does not check the target of the move. You mention this is a minimal example; is there a “real” case that’s failing for you that doesn’t involve a “self” move?

Also, does Rust guarantee that moves can’t panic and are otherwise atomic operations?


#3

Moves are guaranteed to not panic. They’re just a bitwise memory copy.

They’re not atomic, since the memcpy can be interrupted by a context switch.


#4

Moves are guaranteed to not panic. They’re just a bitwise memory copy.

Thanks. I knew they were implemented as bitwise mem copies, but wasn’t sure if that’s a guarantee or the compiler could theoretically do something else that could panic (whether that would be a good implementation choice is a different matter).

They’re not atomic, since the memcpy can be interrupted by a context switch.

Sorry, by “atomic” I really meant “cannot fail”, an expansion on the panic statement - poor wording on my part.


#5

Evaluating the source or destination can still panic (e.g. *vec.last_mut().unwrap() = vec2[0];). If it’s just a plain statement like self.x then it can’t.


#6

Yeah, definitely - I was referring to simple accessors as in @emre’s example.


#7

Thanks for your replies.

I am attempting to make a simple game to learn Rust - the code in question was something similar to self.velocity = self.velocity * 2.0;, where velocity is a simple 2D vector struct. I solved the problem by defining an additional Mul implementation for vector references and then lending the reference to * instead, since it was actually a move. Trying to understand the issue brought me to the example above.

As you said, this is perhaps a case of the compiler choosing to be strict rather than doing some in-depth analysis.


#8

You might like MulAssign for that particular case.


#9

Any reason the vector isn’t Copy? Copying two scalars takes basically no time at all.


#10

And adding on to what @minno said about using copy, in most cases and definitely for this specific line the compiler should optimise all copying out and end up with the exact same assembly as MulAssign would end up with.