Search code examples
rustownership

How to fix `closure requires unique access to `*self` but it is already borrowed`?


I see the error for this code:

error[E0500]: closure requires unique access to `*self` but it is already borrowed
  --> chat/src/user.rs:47:23
   |
   |           return self
   |  ________________-
   | |             .token_to_user_id
   | |_____________________________- borrow occurs here
   |               .get_mut(token)
   |               .and_then(|id| self.find_by_id_mut(id));
   |                -------- ^^^^ ---- second borrow occurs due to use of `*self` in closure
   |                |        |
   |                |        closure construction occurs here
   |                first borrow later used by call

I do understand what the error says and that my code is breaking the rules of borrowing because it has two mutable borrows, however, I don't understand how should code in Rust be written to handle methods on data structures which at first read something, and then mutate it. How to solve such situations?

use std::collections::HashMap;

#[derive(Copy, Clone, Default, PartialEq, Eq, Hash, Debug)]
pub struct UserId(pub u32);

#[derive(Default)]
pub struct UserIndex {
    admin: User,
    id_to_user: HashMap<UserId, User>,
    name_to_user_id: HashMap<String, UserId>,
    token_to_user_id: HashMap<String, UserId>,
}

impl UserIndex {
    pub fn insert_user(&mut self, user: User) {
        let id = user.id;
        self.id_to_user.insert(id, user.clone());
        self.token_to_user_id.insert(user.token.clone(), id);
        match user.user_name {
            None => {
                self.admin = user;
            }
            Some(name) => {
                self.name_to_user_id.insert(name, id);
            }
        }
    }

    pub fn ban_user(&mut self, id: UserId) -> bool {
        if let Some(user) = self.find_by_id_mut(&id) {
            user.is_banned = true;
            return true;
        }
        return false;
    }

    fn find_by_id_mut(&mut self, id: &UserId) -> Option<&mut User> {
        return self.id_to_user.get_mut(id);
    }

    fn find_by_token_mut(&mut self, token: &String) -> Option<&mut User> {
        return self
            .token_to_user_id
            .get_mut(token)
            .and_then(|id| self.find_by_id_mut(id));
    }
}

#[derive(Default, Clone)]
pub struct User {
    pub id: UserId,
    // admin doesn't have name and password
    pub user_name: Option<String>,
    pub password: Option<String>,
    pub token: String,
    pub is_banned: bool,
}

Solution

  • You can't reference the UserId in UserIndex when you try to get mutable access to the index, lucklily UserId is Copy and you can just copy it:

        fn find_by_token_mut(&mut self, token: &String) -> Option<&mut User> {
            self
                .token_to_user_id
                .get(token)
                .copied()
                .and_then(|id| self.find_by_id_mut(&id))
        }
    

    (You also didn't need get_mut here)