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,
}
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)