1

Consider this example:

struct Item {
    x: u32,
}

impl Item {
    pub fn increment(self, amount: u32) -> Self {
        Item { x: self.x + amount }
    }
}

struct Container {
    item: Item,
}

impl Container {
    pub fn increment_item(&mut self, amount: u32) {
        // This line causes "cannot move out of borrowed content"
        self.item = self.item.increment(amount);
    }
}

As you can see, Item.increment consumes the item and returns a new instance.

In Container.increment_item I want to replace the current item with the one returned by Item.increment but the compiler yells at me with a cannot move out of borrowed content error.

In Container.increment_item self is mut so I can mutate its fields, I don't understand why the compiler doesn't allow me to do it.

I know that I can make Container.increment_item consumes self and return a new object, like Item.increment does, and it works, but I would like to understand why I'm getting the error and how can I fix it when I really can't consume the container.

Andrey Tyukin
  • 43,673
  • 4
  • 57
  • 93
Terseus
  • 2,082
  • 15
  • 22

2 Answers2

2
  • Item::increment expects self by value, it moves the Item on which it is invoked.
  • Container::increment_item takes &mut self by reference, it allows you to mutate self, but it does not allow you to take ownership of self (or of any of its parts).
  • When you invoke self.item.increment(amount), you are trying to pass self.item by value, thus moving ownership to the Item::increment function, but you are not allowed to do this with references to values that you don't own.

Just pass self to Item::increment by mutable reference, that's exactly what mutable references are for:

struct Item {
    x: u32,
}

impl Item {
    pub fn increment(&mut self, amount: u32) {
        self.x += amount;
    }
}

struct Container {
    item: Item,
}

impl Container {
    pub fn increment_item(&mut self, amount: u32) {
        self.item.increment(amount);
    }
}

If you insist on taking ownership of Item, then you could use mem::replace:

use std::mem;

struct Item {
    x: u32,
}

impl Item {
    pub fn increment(self, amount: u32) -> Self {
        Item { x: self.x + amount }
    }
}

struct Container {
    item: Item,
}

impl Container {
    pub fn increment_item(&mut self, amount: u32) {
        self.item = mem::replace(&mut self.item, Item { x: 0 }).increment(amount);
    }
}

but it seems unnecessarily complicated in this case.

Andrey Tyukin
  • 43,673
  • 4
  • 57
  • 93
  • 1
    _(or of any of its parts)_ Thanks, that was what I missed about ownership in structs, now I understand the error. Unfortunately in my real world case `Item` (and therefore `Item::increment`) comes from a third-party API so I can't just make it `mut` there, also it's not easy to construct a new instance of `Item` so the `mem::replace` solution it's not what I were expecting. I'm going to open a new question about this specific case. – Terseus Jan 21 '19 at 15:21
-1

increment_item() takes Container by reference and item cannot be moved (or "consumed") while it is behind a reference since increment() takes Item by value. Quickest way to fix this is to make Item a Copy type. This will trigger a copy and not a move (i.e consume). playground

#[derive(Clone, Copy)]
struct Item {
    x: u32,
}

For more, please refer Copy

hellow
  • 12,430
  • 7
  • 56
  • 79
vikram2784
  • 783
  • 7
  • 14