Reputation: 2027
Consider the following simple example:
pub struct Bar {
curr: Vec<i32>,
prev: Vec<i32>,
}
pub fn main() {
let mut b = Bar { curr: vec![1, 2, 3], prev: vec![2, 3, 4] };
foo(&mut b);
}
pub fn foo(bar: &mut Bar) {
let next = vec![3, 4, 5];
bar.prev = bar.curr;
bar.curr = next;
}
The use of Vec
doesn't matter; the point is that Bar
has two fields which don't implement Copy
. This doesn't compile:
error[E0507]: cannot move out of borrowed content
--> derp.rs:15:16
|
15 | bar.prev = bar.curr;
| ^^^ cannot move out of borrowed content
It's not hard to see why: by moving bar.curr
without replacing it immediately, we have to move bar
itself, which we're not allowed to do, as it's only borrowed mutably, not actually owned.
However this is a very common use case (in this case -- keeping the last two outputs of a function, for example) and I feel like there must be an idiomatic Rust use case. I realize this can be gotten around by using a single tuple (curr, prev)
and assigning it at once, but (supposing the function foo
was written long after the struct Bar
has been in use) the refactoring could be pretty frustrating.
Assigning two values at once doesn't seem legal: the code (bar.prev, bar.curr) = (bar.curr, next)
doesn't compile as the left side isn't a legal left-hand-side value.
It is somewhat interesting that the following code compiles:
pub struct Bar {
curr: Vec<i32>,
prev: Vec<i32>,
}
pub fn main() {
let b = Bar { curr: vec![1, 2, 3], prev: vec![2, 3, 4] };
foo(b);
}
pub fn foo(mut bar: Bar) -> Bar {
let next = vec![3, 4, 5];
bar.prev = bar.curr;
bar.curr = next;
bar
}
While the line bar.prev = bar.curr
seems to require move privileges, it doesn't seem to use them, as the following line bar.curr = next
shouldn't compile if bar
has been moved.
Additionally, if you take the bar.curr = next
line out, it no longer compiles (bar
is returned after being moved), so it seems like the compiler is smart enough to figure out how to resolve this issue (that the fields all end up stably assigned), but can't do the same task for mutable pointers.
So I guess (a) is this a bug, (b) is this a known bug, and (c) is there a workaround so I can still do this with mutable pointers?
Upvotes: 1
Views: 869
Reputation: 59005
Use std::mem::replace
or std::mem::swap
.
pub fn foo(bar: &mut Bar) {
use std::mem;
let next = vec![3, 4, 5];
bar.prev = mem::replace(&mut bar.curr, next);
}
It is somewhat interesting that the following code compiles [...]
This is because you own the structure, so the compiler can safely break it apart. It can't do this when the structure is borrowed or behind some kind of pointer. The key question is: what happens if you panic half-way through a modification (answer: it's possible code higher in the call stack could observe the invalid value, and Rust won't allow that to happen).
This isn't a bug, it's just how Rust works.
Upvotes: 6
Reputation: 42759
You can use std::mem::swap
:
pub fn foo(bar: &mut Bar) {
let next = vec![3, 4, 5];
std::mem::swap(&mut bar.prev, &mut bar.curr);
bar.curr = next;
}
Upvotes: 2