I have a hashmap in rust that is keyed to a hashset of strings
I want to implement a method that insert strings to an existing key, if it doesn't exist, created an empty entry.
I have the follow code snippet, I really like how there's only 1 lookup
let mut map: HashMap<&str, HashSet<&str>> = HashMap::new();
let park1 = "poneyland";
let horse = "foo";
map.entry(park1)
.or_insert_with(|| HashSet::from([horse]))
.insert(horse);
But I want to make HashSet
maximum size of 25.
To do so, I tried the following
match map.entry(park1){
Entry::Occupied(x) => {
let tmp_entry = x.into_mut();
if tmp_entry.len() < 25 {
Ok(tmp_entry.insert(horse))
}
else{
Err("maximum size reached")
}
}
Entry::Vacant(y) => {
let tmp_hash_set = HashSet::from([horse]);
Err(y.insert(tmp_hash_set))
}
};
}
But I get the following error
22 | match map.entry(park1){
| ---------------------- `match` arms have incompatible types
...
25 | / if tmp_entry.len() < 64{
26 | | Ok(tmp_entry.insert(horse))
27 | | }
28 | | else{
29 | | Err("maximum size reached")
30 | | }
| |_________- this is found to be of type `Result<bool, &str>`
...
34 | Err(y.insert(tmp_hash_set))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ in mutability
|
= note: expected enum `Result<bool, &str>`
found enum `Result<_, &mut HashSet<&str>>`
Update, per @Ry comments, I did the following:
let current_size = map.len();
match map.entry(park1){
Entry::Occupied(x) => {
let tmp_entry = x.into_mut();
if tmp_entry.len() < 64{
Ok(tmp_entry.insert(horse))
}
else{
Err("maximum horses reached in park")
}
}
Entry::Vacant(y) => {
if current_size == 9999 {
Err("maximum number of parks reached")
}
else{
let tmp_hash_set = HashSet::from([horse]);
y.insert(tmp_hash_set);
Ok(true)
}
}
};
The above works, I'm curious if there's a better way?
The above works, I'm curious if there's a better way?
Minute improvements are possible though the gist seems sound:
You can lift the conditions as match guards, which obviates some of the blocks (brackets) and makes the code more declarative:
match map.entry(park1) {
Entry::Occupied(x) if x.get().len() < 64 =>
Ok(x.into_mut().insert(horse)),
Entry::Occupied(_) => Err("maximum horses reached in park"),
Entry::Vacant(_) if current_size == 9999 =>
Err("maximum number of parks reached"),
Entry::Vacant(y) => {
let tmp_hash_set = HashSet::from([horse]);
y.insert(tmp_hash_set);
Ok(true)
}
};
You could also reorganise to e.g. put both the error cases up top for clarity though that's more of a taste thing:
match map.entry(park1) {
Entry::Occupied(x) if x.get().len() >= 64 =>
Err("maximum horses reached in park"),
Entry::Vacant(_) if current_size == 9999 =>
Err("maximum number of parks reached"),
Entry::Occupied(x) =>
Ok(x.into_mut().insert(horse)),
Entry::Vacant(y) => {
let tmp_hash_set = HashSet::from([horse]);
y.insert(tmp_hash_set);
Ok(true)
}
};
The tmp_hash_set
is not necessary:
y.insert(HashSet::from([horse]));
Ok(true)