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

ghz 8months ago ⋅ 74 views

How to fix closure requires unique access to*selfbut 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,
}

Answers

In Rust, you can't have two mutable borrows of the same object at the same time. This rule is enforced by the borrow checker to prevent data races and ensure memory safety.

To solve this issue, you can use a different approach to avoid borrowing self mutably twice in the same scope. One common approach is to split the mutable borrow into two separate scopes, ensuring that they don't overlap.

Here's how you can refactor your code to achieve this:

impl UserIndex {
    // Other methods...

    pub fn ban_user(&mut self, id: UserId) -> bool {
        // Borrow self once to get a mutable reference to the user by id
        if let Some(user) = self.find_by_id_mut(&id) {
            // Inside this scope, self is not borrowed anymore
            // You can safely mutate the user
            user.is_banned = true;
            return true;
        }
        return false;
    }

    fn find_by_token_mut(&mut self, token: &String) -> Option<&mut User> {
        // Borrow self once to get a mutable reference to the user id by token
        if let Some(id) = self.token_to_user_id.get(token) {
            // Borrow self again to get a mutable reference to the user by id
            // Since the first borrow is dropped, self is not borrowed anymore
            if let Some(user) = self.find_by_id_mut(id) {
                return Some(user);
            }
        }
        None
    }
}

In this refactored code:

  • In the ban_user method, we borrow self once to get a mutable reference to the user by id. Inside the scope of this borrow, we can safely mutate the user. After this scope, self is not borrowed anymore.
  • In the find_by_token_mut method, we borrow self twice in separate scopes. First, we borrow self to get a mutable reference to the user id by token. Then, inside a new scope, we borrow self again to get a mutable reference to the user by id.

By splitting the mutable borrows into separate scopes, we ensure that self is not borrowed mutably at the same time in the same scope, resolving the borrow checker error.