Я вижу ошибку для этого кода:
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
Я понимаю, о чем говорит ошибка, и что мой код нарушает правила заимствования, поскольку он имеет два изменяемых заимствования, однако я не понимаю, как следует писать код на Rust для обработки методов в структурах данных, которые сначала что-то читают, а затем мутировать его. Как решить такие ситуации?
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,
}
Вы не можете ссылаться на UserId
в UserIndex
, когда пытаетесь получить изменяемый доступ к индексу, к счастью, UserId
есть Copy
, и вы можете просто скопировать его:
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)
Не обязательно, я бы, вероятно, использовал Rc
/Arc
, чтобы избежать копирования дорогостоящей структуры, или, возможно, использовал бы usize
с Vec
или что-то в этом роде, все зависит от точных деталей. Вы определенно не можете использовать ссылку.
Спасибо, если бы это был какой-то тяжелый тип, где только одно поле является ссылкой на другую карту, мне также нужно было бы скопировать результат чтения?