Cannot move field out of borrowed content using &mut


#1

Hello,

I made this minimal example which outlines my problem:

// Foo is from an external crate, I have no control over it.
struct Foo {

}

impl Foo {
    fn foo(self) -> Self {
        self
    }
}

// Bar is part of my project.
struct Bar {
    f: Foo
}

impl Bar {
    fn bar(&mut self) {
        self.f = self.f.foo();
    }
}

fn main() {
    let mut b = Bar { f: Foo { } };
    b.bar();
}

So I’m trying to change f: Foo from b: Bar using the function Foo::foo. To me this seems reasonable. I have a mutable borrow of self. I can’t consume self but I can do whatever I want with the insides of self.
It gives me this error:

error[E0507]: cannot move out of borrowed content
  --> main.rs:17:18
   |
17 |         self.f = self.f.foo();
   |                  ^^^^ cannot move out of borrowed content

My questions are:

  1. Why does the error message claim that self cannot be moved? I want to move f, not self.
  2. Is this even possible? I fiddled with it for some time but couldn’t get it to work.
  3. If it is possible, how? If it’s not then why? What is the key point that I’m missing here?

#2

The best way to get around that is to have bar take self, and then return self after reassigning self.foo.

// Foo is from an external crate, I have no control over it.
struct Foo {

}

impl Foo {
    fn foo(self) -> Self {
        self
    }
}

// Bar is part of my project.
struct Bar {
    f: Foo
}

impl Bar {
    fn bar(mut self) -> Self {
        self.f = self.f.foo();
        self
    }
}

fn main() {
    let mut b = Bar { f: Foo { } };
    let b = b.bar();
}

#3

This is a straightforward solution but what if I don’t want this behaviour?


#4

There are a lot of ways to get around this. A particularly ugly one is this:

fn bar(&mut self) {
    let mut tmpFoo = Foo { };
    std::mem::swap(&mut tmpFoo, &mut self.f);
    let mut tmpFoo = tmpFoo.foo();
    std::mem::swap(&mut tmpFoo, &mut self.f);
}

#5

f is part of self since Bar owns it; f needs to move because its foo() wants it by value. Since you only have a mutable borrow (i.e. &mut self) of Bar, you cannot move out of it.

What you can do here is store f in an Option:

struct Bar {
    f: Option<Foo>
}

impl Bar {
    fn bar(&mut self) {        
        let f = self.f.take().unwrap();
        self.f = Some(f.foo());
    }
}

#6

This is nice. However, now my question is how does Option do it? Fortunately we have access to the source:

    pub fn take(&mut self) -> Option<T> {
        mem::replace(self, None)
    }

So I could mirror its behaviour and avoid using Option, as long as I can construct a trivial Foo:

use std::mem;

struct Foo {

}

impl Foo {
    fn foo(self) -> Self {
        self
    }
}

struct Bar {
    f: Foo
}

impl Bar {
    fn bar(&mut self) {
        let f = mem::replace(&mut self.f, Foo { });
        self.f = f.foo();
        // oneliner:
        // self.f = mem::replace(&mut self.f, Foo { }).foo();
    }
}

fn main() {
    let mut b = Bar { f: Foo { } };
    b.bar();
}

Thank you.


#7

Yeah, if the real usecase allows for a trivial Foo then that’s the way to go.


#8

You can always use mem::uninitialized() if Foo isn’t trivial to construct, seeing whatever you’re putting there doesn’t ever get used… This could come back to bite you if Foo::foo() panics though, seeing as now your Bar is in an inconsistent state (see Pre-Pooping Your Pants With Rust for ways you can deal with this).