2

I have a struct and respective implementation:

pub struct Departments {
    pub id: String,
    pub linked_accounts: Vec<Account>,
}

impl Departments {
    pub fn add_account(&mut self, acct: Account) -> Self {
        let mut vec: Vec<Account> = self.linked_accounts;
        vec.push(acct);

        Self {
            id: self.id,
            linked_accounts: vec,
        }
    }
}
error[E0507]: cannot move out of `self.linked_accounts` which is behind a mutable reference
  --> src/lib.rs:10:37
   |
10 |         let mut vec: Vec<Account> = self.linked_accounts;
   |                                     ^^^^^^^^^^^^^^^^^^^^ move occurs because `self.linked_accounts` has type `Vec<Account>`, which does not implement the `Copy` trait
   |
help: consider borrowing here
   |
10 |         let mut vec: Vec<Account> = &self.linked_accounts;
   |                                     +

error[E0507]: cannot move out of `self.id` which is behind a mutable reference
  --> src/lib.rs:14:17
   |
14 |             id: self.id, //*Error here*
   |                 ^^^^^^^ move occurs because `self.id` has type `String`, which does not implement the `Copy` trait

I use it when loading the department from the DB, adding account, and storing it back to the DB:

match db.get::<Departments>(id).await? {
    None => bail!("No records found"),
    Some(departments) => {
        let mut departments = departments;

        departments.add_account(Account::Credit);

        let id = Id::new(Departments::KIND, &id);
        gh.update::<_, Departments>(id, departments.clone()).await?;
    }
}

How can I fix this?

8
  • This is probably a XY problem. Anyway, we need more details. Commented Jul 6, 2022 at 9:19
  • I need to append a vector 'Account' value inside struct 'Departments', which is very straight forward..i am calling as 'dept.add_account(Account::EnumVal)' where Account as enum Commented Jul 6, 2022 at 9:25
  • I meant more details for why you specifically need to call with &mut self and return an owned Self. Commented Jul 6, 2022 at 9:25
  • main goal is not to pass the ownership from calling area, where i might need to assign a new variable, so i am passing as '&mut self' Commented Jul 6, 2022 at 9:29
  • Please provide more context. An example of the calling code will be good. Commented Jul 6, 2022 at 9:30

2 Answers 2

7

There are a couple of ways to interpret your question. In all the cases, the answer starts with "Your function signature is wrong", but what exactly it should be, depends on your usecase.

But most certainly, it should be one of:

  • pub fn add_account(&mut self, acct: Account)
  • pub fn add_account(&mut self, acct: Account) -> &mut Self
  • pub fn add_account(mut self, acct: Account) -> Self

I think the biggest misconception you are having is that you think you need to return Self. The way you are using add_account, by simply calling it on departments.add_account(Account::Credit) does not require you to return Self. You can directly modify the self object:

pub struct Departments {
    pub id: String,
    pub linked_accounts: Vec<Account>,
}

impl Departments {
    pub fn add_account(&mut self, acct: Account) {
        self.linked_accounts.push(acct);
    }
}

Now, there is a usecase where you want to return some form of Self, and that is if you want to be able to chain those calls:

departments
    .add_account(Account::Credit)
    .add_account(Account::Debit)
    .add_account(Account::Savings);

This is usually done by returning &mut Self:

pub struct Departments {
    pub id: String,
    pub linked_accounts: Vec<Account>,
}

impl Departments {
    pub fn add_account(&mut self, acct: Account) -> &mut Self {
        self.linked_accounts.push(acct);
        self
    }
}

Note that this still doesn't require you to instantiate a new Self object, like you were doing in your code.


The actual error

In case you want to understand the original error:

  • The &mut self argument means that you only borrow self, and you have to give it back at some point. (happens automatically at the end of the function)
  • let mut vec: Vec<Account> = self.linked_accounts; moves the linked_accounts member out of self. This is only possible when we take ownership of self instead of borrowing it, because then, we can dismantle it to our pleasing. But as we need to give it back, we cannot just move members out of it.

I didn't talk about the actual error in more detail, because it was just an artifact of the incorrect function signature and didn't really contribute meaningfully to a solution.

Either way, I think there are a couple of misunderstandings of yours as to how ownership works, so I recommend reading the ownership chapter of the Rust book.

The reason why I think you have misunderstandings, is because it is literally impossible to return Self if your function signature contains &mut self. Self is owned, and you cannot create an owned object from a borrowed one without copying it.

Yes, you are trying to create a copy of it in your attempt to implement it, but the usage example you posted shows me that you don't actually want to create a copy.

Sign up to request clarification or add additional context in comments.

2 Comments

There can be cases when you do want to return an owned self (you've deconstructed and reconstructed it inside to do some typestate). It seems the answer is that it's not possible.
Sure you can return an owned object of type Self, but you cannot return ownership to self if it is passed by reference. Note the capitalization, one is a type, and one is an object.
-1

In a meantime you can also Clone Self.

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=45c902d55a8f3e190893f82f720fdae2

1 Comment

As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.