Reputation: 5524
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?
Upvotes: 1
Views: 87
Reputation: 11187
One option is to use the take_mut
crate, which offers the take
functions that do exactly what you want:
take
allows for takingT
out of a&mut T
, doing anything with it including consuming it, and producing anotherT
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 validT
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));
}
}
Upvotes: 1
Reputation: 65907
Do it the other way around:
impl Widget {
fn op(mut self, rhs: Widget) -> Self {
self.update(rhs);
self
}
fn update(&mut self, rhs: Widget) {
// Some stuff
}
}
You cannot move out of a borrowed pointer, because moving makes the source unusable, but since you don't own the source, that information would have to propagate back to the owner, and Rust doesn't support that.
You might say "but I'm assigning a new value to *self
before the function returns!". The problem with that is that if there's a panic between the move and the assignment, *self
would still be left with no valid value. This is especially problematic if dropping self
is not a no-op (though Rust doesn't care if dropping is a no-op or not here).
Upvotes: 3