I'd highly recommend for everyone to read the chapter 4 of "the book" before writing any Rust code. Other chapters are awesome, but the ch.4 is must read.
Short answer for your question is that the &T is a shared borrow of the type T, and the &mut T is a unique borrow of the type T. For longer answer, please read the ch.4 first!
There're several reasons why it's not trivial to fix this simple code. One of them is, the HashMap cannot be mutated concurrently.
It is a common decision between many languages since the thread safe hashmap is a lot slower than the non-thread-safe one, and in the vast majority of use cases the maps are already uniquely accessed by a certain thread on modification. Only difference between the Rust and other languages is, in Rust you'll get compile error instead of some weird and inconsistent runtime behavior.
As mentioned previously, you cannot mutate self inside a method that takes &self - you'll need to change it to &mut self.
Calling map on an iterator by itself doesn't do anything, as Rust's iterators are lazy. You need to call a method that actually drives the iterator in order for it to actually run. In this case, you're not actually mapping the contents of hashmap1, you just want to run the closure for its side effects, so you should replace map with for_each, which does drive the iterator to completion.
If you use self directly inside a closure, it will be captured by that closure, preventing you from mutating any of the other fields of self elsewhere. You can force the closure to only capture the hashmap2 field by adding a variable binding and then using that in the closure:
However, this exposes another issue with your code - you cannot mutate a reference to h2 from inside a parallel iterator! Each time your closure gets called, it could be on a seperate thread, so there's no guarentee that multiple threads wouldn't try to mutate h2 at the same time. As @Hyeonu suggested, you'd need to use some sort of thread-safe hash map to be able to do that, or wrap it in a Mutex or something like that. Or, you could just not use a parallel iterator.
Here's a non-parallel version of the code that works:
And here's a parallel version of the code that works - note that it uses locking to make the mutation of h2 thread safe; using a concurrent data structure would likely be more effecient:
use rayon::iter::IntoParallelRefIterator;
use rayon::iter::ParallelIterator;
use std::collections::HashMap;
use std::sync::Mutex;
struct MyStruct {
pub hashmap1: HashMap<String, f32>,
pub hashmap2: Mutex<HashMap<String, f32>>,
}
impl MyStruct {
pub fn add(&mut self) {
// You don't need a mutable reference to mutate something
// that's inside a Mutex - the locking enforces Rust's
// aliasing rules at runtime.
let h2 = &self.hashmap2;
self.hashmap1.par_iter().for_each(|(k, v)| {
// This will block other threads from using `hashmap2`
// until the closure returns and the lock is dropped.
let mut lock = h2.lock().unwrap();
lock.insert(k.to_string(), *v + 1.0);
});
}
}