Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to replace the value of a struct field without taking ownership [duplicate]

Tags:

rust

ownership

(Following "cannot move out of borrowed content" when replacing a struct field)

I have a third-party API where one struct have a method that consumes the instance and returns a new instance; I want to wrap this API in my own wrapper and abstract away the detail that the struct is consumed in the operation.

This example explains what I'm trying to achieve:

// Third-party API
struct Item {
    x: u32,
}

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

// My API
struct Container {
    item: Item,
}

impl Container {
    pub fn increment_item(&mut self, amount: u32) {
        // This line causes "cannot move out of borrowed content" but this is exactly what I want to do
        self.item = self.item.increment(amount);
    }
}

While now I understand the error I'm wondering how can I implement this without taking ownership of self inside Container::increment_item.

Proposed solutions:

  • Change Item::increment to take &mut self instead of self: I can't, Item::increment comes from a crate.
  • Use mem::replace (from here): Unfortunately constructing an Item instance it's not that easy, and to be honest I don't completely understand how the mem::replace solutions works.
  • Change Container::increment_item to take self instead of &mut self: I don't want to consume the Container instance in the process, I'm trying to design my wrapper to be as ergonomic as possible and abstract away completely the fact that Item must be consumed when changing it.

Some ideas on how to do it? Or am I trying an impossible design in Rust?

like image 337
Terseus Avatar asked Sep 06 '25 09:09

Terseus


1 Answers

The simplest way is to use an Option:

  • use take to take ownership of the item,
  • then assign back Some(...).

If a panic occurs while the Option is empty, this is perfectly safe. No double-destruction occurs, and the container can even remain usable if you so desire.

Or in code:

// Third-party API
struct Item {
    x: u32,
}

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

// My API
struct Container {
    item: Option<Item>,
}

impl Container {
    pub fn increment_item(&mut self, amount: u32) {
        let item = self.item.take().unwrap();
        self.item = Some(self.item.increment(amount));
    }
}
like image 146
Matthieu M. Avatar answered Sep 09 '25 06:09

Matthieu M.