Reputation: 11
I have this, which works:
let clients: &mut HashMap<usize, _some_none_clonable_type> = &mut self.clients;
let keys = {
let mut keys = Vec::<usize>::new();
for key in clients.keys(){
keys.push(*key);
}
keys
};
if let Some(client) = clients.get_mut(&id) {
println!("INFO: The !users macro was sent from user: {id}.");
client.write_all(format!("User ID's:\n").as_bytes())?;
for user_id in keys {
client.write_all(format!("{user_id}\n").as_bytes())?;
}
}
But what I actually want is this:
// first mutable borrow of hashmap
if let Some(client) = self.clients.get_mut(&id) {
println!("INFO: The !users macro was sent from user: {id}.");
client.write_all(format!("User ID's:\n").as_bytes())?;
// immutable borrow of the same map in the same scope:
for user_id in self.clients.keys() {
client.write_all(format!("{user_id}\n").as_bytes())?;
}
}
Correct me if I'm wrong but since we don't mutate the keys and we never remove anything, this code should be valid, just not well typed. Of course the compiler complains. Is there a way to circumvent the type checker by using indirections or unsafe? I realize that it's not necessary because I could just check if the entry exists in the hash map and only then borrow, but I'm actually more interested in this type of scenario in general.
Upvotes: 1
Views: 43
Reputation: 560
When you call self.clients.get_mut(&id)
, you call HashMap::get_mut
. This takes an &mut self
, which means that the borrow checker assumes that the function will return a value that could mutate any part of the HashMap. For example, the value it returns (client
) could, upon calling its write_all
method, delete the keys of the HashMap while you were iterating over it. The borrow checker doesn't know any better; it's just looking at the function signatures.
At its core, the problem is that you need to mutably borrow the values of a HashMap while immutably borrowing its keys. The HashMap methods don't allow you to do this because it adds complexity, but you could easily test it out yourself with two Vec
s, one that holds the keys and one that holds the corresponding values. Of course this makes it a lot harder to work with it, since you now need to handle the keys and values separately. HashMap gives you a simpler interface at the cost of making some operations much harder.
Upvotes: 0