swapping two entries of a HashMap - rust

i have a simple HashMap; say HashMap<char, char>.
is there a way to swap two elements in this hashmap using std::mem::swap (or any other method)?
Of course there is the simple way getting the values with get and then replace them with insert - but that would trigger the hasher twice (once for getting then for inserting) and i was looking for a way to side-step the second hasher invocation (more out of curiosity than for performance).
what i tried is this (in several versions; none of which worked - and as remarked in the comments: entry would not do what i expect even if i got this past the compiler):
use std::collections::HashMap;
use std::mem::swap;
let mut hash_map: HashMap<char, char> = HashMap::default();
hash_map.insert('A', 'Z');
hash_map.insert('B', 'Y');
swap(&mut hash_map.entry('A'), &mut hash_map.entry('B'));
now the compiler complains (an i understand why it should)
error[E0499]: cannot borrow `hash_map` as mutable more than once at a time
--> tests.rs:103:42
|
103 | swap(&mut hash_map.entry('A'), &mut hash_map.entry('B'));
| ---- -------- ^^^^^^^^ second mutable borrow occurs here
| | |
| | first mutable borrow occurs here
| first borrow later used by call
also just getting the two values this way fails in more or less the same way:
let mut a_val = hash_map.get_mut(&'A').expect("failed to get A value");
let mut b_val = hash_map.get_mut(&'B').expect("failed to get B value");
swap(&mut a_val, &mut b_val);
is there a way to simply swap two entries of a HashMap?

I can't see any safe way to do it:
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert('A', 'Z');
map.insert('B', 'Y');
let a = map.get_mut(&'A').unwrap() as *mut char;
let b = map.get_mut(&'B').unwrap() as *mut char;
unsafe {
std::ptr::swap(a, b);
}
assert_eq!(map.get(&'A'), Some(&'Y'));
assert_eq!(map.get(&'B'), Some(&'Z'));
}

There is one completely safe way I can think of to do this safely, but it's super inefficient: what you want is to get two &mut values, which means borrowck needs to know they're nonoverlapping. Missing a builtin along the lines of split_mut (or the collection being handled specially), the only way I see is to mutably iterate the entire collection, keep refs to the items you're interested in, and swap that:
let mut h = HashMap::new();
h.insert("a", "a");
h.insert("b", "b");
let mut it = h.iter_mut();
let e0 = it.next().unwrap();
let e1 = it.next().unwrap();
std::mem::swap(e0.1, e1.1);
println!("{:?}", h);
It requires a linear traversal of the map until you've found the entries whose values you want to swap though. So even though this has the advantage of not hashing at all edwardw's is answer is probably more practical.

Related

How do I modify values in a HashMap in Rust?

If I had a Hashmap in Rust and I wanted to get a value and modify it, for example, lets say the value is a type u32 and I want to increment it, what is the best way to do that? I found an example that works but I am wondering of there a "Best Practices" way to do it. The example I found that does the job is:
`use std::collections::HashMap;
use std::cell::RefCell;
fn main() {
let mut map = HashMap::new();
map.insert("Key".to_owned(), RefCell::new(0));
let value = map.get("Key").unwrap();
*value.borrow_mut() += 1;
println!("{:?}", value.borrow());
}`
Which worked for me, but I was suspicious of using RefCell for this. Is there a better way to do it? Thanks.
Yeah, I'm suspicious of that RefCell too. You'd use that if you had a very specific requirement, such as the interior mutability capabilities of RecCell.
I don't see why you can't just use the example code and ditch the RefCell.
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert("Key".to_owned(), 0);
let value = map.get_mut("Key").unwrap();
*value += 1;
println!("{:?}", value);
let read_value = map.get("Key").unwrap();
println!("{:?}", read_value);
}
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=a23a90c4df7eb010945980b9b95eb031
RefCell provides run time borrow checking, as opposed to compile time borrow checking which you would otherwise get.
In many cases you do not need that - you can just use get_mut as suggested in the comments and by #cadolphs.
However if you need to get mutable access to individual elements within the map at the same time, you might use RefCell. For example, consider this code:
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert("Key".to_owned(), 0);
map.insert("Key2".to_owned(), 1);
let value = map.get_mut("Key").unwrap();
let value2 = map.get("Key2").unwrap();
*value += *value2;
println!("{:?}", *value);
}
This will fail to compile because I am trying to get a second value from the hashmap while I am still holding a mutable reference to the first:
error[E0502]: cannot borrow `map` as immutable because it is also borrowed as mutable
--> src/main.rs:8:18
|
7 | let value = map.get_mut("Key").unwrap();
| ------------------ mutable borrow occurs here
8 | let value2 = map.get("Key2").unwrap();
| ^^^^^^^^^^^^^^^ immutable borrow occurs here
9 | *value += *value2;
| ----------------- mutable borrow later used here
You could solve that using RefCell like this:
use std::collections::HashMap;
use std::cell::RefCell;
fn main() {
let mut map = HashMap::new();
map.insert("Key".to_owned(), RefCell::new(0));
map.insert("Key2".to_owned(), RefCell::new(1));
let value = map.get("Key").unwrap();
let value2 = map.get("Key2").unwrap();
*value.borrow_mut() += *value2.borrow();
println!("{:?}", *value);
}
Here we can get the value we intend to modify out of the hash using get rather than get_mut, so we are not borrowing the hash mutably. The hash itself is not being modified, just the values inside it - this is the pattern referred to in the Rust community as interior mutability.
This pattern should be used very sparingly though, only when really needed.
For one thing, you are trading a compile time check for a run time check. If you have made a mistake in your logic, you won't find out at compile time, you will find out when the code panics at runtime! You can work around that by using the try_borrow* versions of these methods (eg. try_borrow_mut), which return a Result instead of panicing, but then you need to add error handling to deal with it.
Another reason is that a run time borrow check may harm the performance of your code.
My example above is a case where you can easily avoid the whole thing, because the values in the hashmap are just integers which are Copy, so we can just do this instead:
let value2 = *map.get("Key2").unwrap();
let value = map.get_mut("Key").unwrap();
*value += value2;

Emulate BTreeMap::pop_last in stable Rust 1.65 or older

Editor's note: as of Rust 1.66.0, BTreeMap::pop_last has been stabilized.
In stable Rust 1.65 or older, is there a way to write a function equivalent to BTreeMap::pop_last?
The best I could come up with is:
fn map_pop_last<K, V>(m: &mut BTreeMap<K, V>) -> Option<(K, V)>
where
K: Ord + Clone,
{
let last = m.iter().next_back();
if let Some((k, _)) = last {
let k_copy = k.clone();
return m.remove_entry(&k_copy);
}
None
}
It works, but it requires that the key is cloneable. BTreeMap::pop_last from Rust nightly imposes no such constraint.
If I remove the cloning like this
fn map_pop_last<K, V>(m: &mut BTreeMap<K, V>) -> Option<(K, V)>
where
K: Ord,
{
let last = m.iter().next_back();
if let Some((k, _)) = last {
return m.remove_entry(k);
}
None
}
it leads to
error[E0502]: cannot borrow `*m` as mutable because it is also borrowed as immutable
--> ...
|
.. | let last = m.iter().next_back();
| -------- immutable borrow occurs here
.. | if let Some((k, _)) = last {
.. | return m.remove_entry(k);
| ^^------------^^^
| | |
| | immutable borrow later used by call
| mutable borrow occurs here
Is there a way to work around this issue without imposing additional constraints on map key and value types?
Is there a way to work around this issue without imposing additional constraints on map key and value types?
It doesn't appear doable in safe Rust, at least not with reasonable algorithmic complexity. (See Aiden4's answer for a solution that does it by re-building the whole map.)
But if you're allowed to use unsafe, and if you're determined enough that you want to delve into it, this code could do it:
// Safety: if key uses shared interior mutability, the comparison function
// must not use it. (E.g. it is not allowed to call borrow_mut() on a
// Rc<RefCell<X>> inside the key). It is extremely unlikely that such a
// key exists, but it's possible to write it, so this must be marked unsafe.
unsafe fn map_pop_last<K, V>(m: &mut BTreeMap<K, V>) -> Option<(K, V)>
where
K: Ord,
{
// We make a shallow copy of the key in the map, and pass a
// reference to the copy to BTreeMap::remove_entry(). Since
// remove_entry() is not dropping the key/value pair (it's returning
// it), our shallow copy will remain throughout the lifetime of
// remove_entry(), even if the key contains references.
let (last_key_ref, _) = m.iter().next_back()?;
let last_key_copy = ManuallyDrop::new(std::ptr::read(last_key_ref));
m.remove_entry(&last_key_copy)
}
Playground
Is there a way to work around this issue without imposing additional constraints on map key and value types?
You can't do it efficiently in safe rust, but it is possible:
fn map_pop_last<K, V>(m: &mut BTreeMap<K, V>) -> Option<(K, V)>
where
K: Ord,
{
let mut temp = BTreeMap::new();
std::mem::swap(m, &mut temp);
let mut iter = temp.into_iter();
let ret = iter.next_back();
m.extend(iter);
ret
}
playground
This will do a full traversal of the map but is safe.
We can even be sure that it is not possible to mutate the map in-place with the current offer of interfaces in safe Rust (version 1.59, at the time of writing):
To extract the key, we need to look the keys of the map. "Look at" implies borrowing here. This gives us a &K reference that also borrows the entire map.
Now, a problem arises: To call any of these remove* methods, we need to mutably borrow the map again, which we cannot do since we still hold the reference to the key that we are about to extract - these lifetimes overlap and it won't compile.
Another approach could be to try to get hold of the Entry, but to get one through the .entry method, we need have an owned K too.

Rust check borrow with the whole HashMap, not check the key, is there any good way?

I want move the elements of HashMap<u64, Vec> key=1 to key=2
use std::collections::HashMap;
fn main() {
let mut arr: HashMap<u64, Vec<u64>> = HashMap::new();
arr.insert(1, vec![10, 11, 12]); // in fact, elments more than 1,000,000, so can't use clone()
arr.insert(2, vec![20, 21, 22]);
// in fact, following operator is in recusive closure, I simplify the expression:
let mut vec1 = arr.get_mut(&1).unwrap();
let mut vec2 = arr.get_mut(&2).unwrap();
// move the elements of key=1 to key=2
for v in vec1 {
vec2.push(vec1.pop().unwrap());
}
}
got error:
error[E0499]: cannot borrow `arr` as mutable more than once at a time
--> src/main.rs:10:20
|
9 | let mut vec1 = arr.get_mut(&1).unwrap();
| --- first mutable borrow occurs here
10 | let mut vec2 = arr.get_mut(&2).unwrap();
| ^^^ second mutable borrow occurs here
11 | for v in vec1 {
| ---- first borrow later used here
Rust check borrow with the whole HashMap, not check the key.
Is there any good way ?
It's not clear what the context / constraints are, so depending on those there are various possibilities of different impact and levels of complexity
if you don't care about keeping an empty version of the first entry, you can just use HashMap::remove as it returns the value for the removed key: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=1734142acb598bad2ff460fdff028b6e
otherwise, you can use something like mem::swap to swap the vector held by key 1 with an empty vector, then you can update the vector held by key 2: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=e05941cb4d7ddf8982baf7c9437a0446
because HashMap doesn't have splits, the final option would be to use a mutable iterator, iterators are inherently non-overlapping so they provide mutable references to individual values, meaning they would let you obtain mutable references to both values simultanously, though the code is a lot more complicated: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=87d3c0a151382ce2f47dda59dc089d70
While the third option has to traverse the entire hashmap (which is less efficient than directly finding the correct entries by hashing), it has the possible advantage of not "losing" v1's allocation which is useful if v1 will be filled again in the future: in the first option v1 is completely dropped, and in the second option v1 becomes a vector of capacity 0 (unless you swap in a vector with a predefined capacity, but that's still an extra allocation)
You can put the Vec into a RefCell moving the borrow check to runtime:
use std::cell::RefCell;
use std::collections::HashMap;
fn main() {
let mut arr: HashMap<u64, RefCell<Vec<u64>>> = HashMap::new();
arr.insert(1, RefCell::new(vec![10, 11, 12])); // in fact, elments more than 1,000,000, so can't use clone()
arr.insert(2, RefCell::new(vec![20, 21, 22]));
// in fact, following operator is in recusive closure, I simplify the expression:
let mut vec1 = arr.get(&1).unwrap().borrow_mut();
let mut vec2 = arr.get(&2).unwrap().borrow_mut();
// move the elements of key=1 to key=2
vec2.append(&mut vec1);
}
Tip: Use Vec::append which moves the values from one vector to another.

error[E0597]: borrowed value does not live long enough in While loop

I am really new to Rust, I am having trouble solving this error, but it only happens if I comment out the while statement , basicly I am asking values from the console and storing it in a HashMap:
use std::collections::HashMap;
use std::io;
fn main() {
let mut customers = HashMap::new();
let mut next_customer = true;
while next_customer {
let mut input_string = String::new();
let mut temp_vec = Vec::with_capacity(3);
let mut vec = Vec::with_capacity(2);
println!("Insert new customer f.e = customer id,name,address:");
io::stdin().read_line(&mut input_string);
input_string = input_string.trim().to_string();
for s in input_string.split(",") {
temp_vec.push(s);
}
vec.push(temp_vec[1]);
vec.push(temp_vec[2]);
let mut key_value = temp_vec[0].parse::<i32>().unwrap();
customers.insert(key_value, vec);
next_customer = false;
}
println!("DONE");
}
The code results in the error
error[E0597]: `input_string` does not live long enough
--> src/main.rs:14:18
|
14 | for s in input_string.split(",") {
| ^^^^^^^^^^^^ borrowed value does not live long enough
...
20 | customers.insert(key_value, vec);
| --------- borrow later used here
21 | next_customer = false;
22 | }
| - `input_string` dropped here while still borrowed
As others have said the problem lies with the lifetime and/or type of the values getting put into the customers map.
customers.insert(key_value, vec);
| --------- borrow later used here
Often this happens when the compiler has decided to give an object a type that you didn't expect. To find out what it's doing you can force the type, and see how it complains. Changing the code to:
let mut customers: HashMap<(),()> = HashMap::new();
Gives us two relevant errors:
20 | customers.insert(key_value, vec);
| ^^^^^^^^^ expected `()`, found `i32`
...
20 | customers.insert(key_value, vec);
| ^^^ expected `()`, found struct `std::vec::Vec`
|
= note: expected unit type `()`
found struct `std::vec::Vec<&str>`
So the type that the compiler wants to give our customers object is HashMap<i32, Vec<&str>>
The problem is that the &str lifetime has got to be inside the block as we don't store the Strings anywhere, and they can't have 'static lifetime since they're user input.
This means we probably want a HashMap<i32,Vec<String>>.
Changing the code to use one of those gives us an error about vec not having the right type: It's getting deduced as a Vec<&str>, but we want a Vec<String>.
We have two options.
Convert the vec to the right type just before we insert it into the map using customers.insert(key_value, vec.iter().map(|s| s.to_string()).collect()). (Though you may want to extract it to a variable for clarity).
Explicitly change the type of vec to Vec<String>
Option 1 "just works". While option 2 leads us down a path of making similar changes closer and closer to the read_line call.
Once you've decided on the fix in option 1, you can remove the manual type annotations that were added to work out the fix, if you find them overly noisy.
The issue is that you are passing around reference to underlying &str values that will get dropped. One way is to take the input string, trim and split it, then clone it going into the other vector.
let temp_vec: Vec<String> = input_string.trim().split(",").map(|t| t.to_string()).collect();
vec.push(temp_vec[1].clone());
vec.push(temp_vec[2].clone());

Why does a variable holding the result of Vec::get_mut not need to be mutable?

I have the following code:
fn main() {
let mut vec = Vec::new();
vec.push(String::from("Foo"));
let mut row = vec.get_mut(0).unwrap();
row.push('!');
println!("{}", vec[0])
}
It prints out "Foo!", but the compiler tells me:
warning: variable does not need to be mutable
--> src/main.rs:4:9
|
4 | let mut row = vec.get_mut(0).unwrap();
| ----^^^
| |
| help: remove this `mut`
Surprisingly, removing the mut works. This raises a few questions:
Why does this work?
Why doesn't this work when I use vec.get instead of vec.get_mut, regardless of whether I use let or let mut?
Why doesn't vec work in the same way, i.e. when I use let vec = Vec::new(), why can't I call vec.push()?
vec.get_mut(0) returns an Option<&mut String>, so when you unwrap that value you will have a mutable borrow of a String. Remember, that a let statement's left side is using pattern matching, so when your pattern is just a variable name you essentially say match whatever is on the right and call it name. Thus row matches against &mut String so it already is mutable.
Here's a much simpler and more straightforward example to illustrate the case (which you can try in the playground):
fn main() {
let mut x = 55i32;
dbg!(&x);
let y = &mut x; // <-- y's type is `&mut i32`
*y = 12;
dbg!(&x);
}

Resources