Default mutable value from HashMap - rust

Suppose I have a HashMap and I want to get a mutable reference to an entry, or if that entry does not exist I want a mutable reference to a new object, how can I do it? I've tried using unwrap_or(), something like this:
fn foo() {
let mut map: HashMap<&str, Vec<&str>> = HashMap::new();
let mut ref = map.get_mut("whatever").unwrap_or( &mut Vec::<&str>::new() );
// Modify ref.
}
But that doesn't work because the lifetime of the Vec isn't long enough. Is there any way to tell Rust that I want the returned Vec to have the same lifetime as foo()? I mean there is this obvious solution but I feel like there should be a better way:
fn foo() {
let mut map: HashMap<&str, Vec<&str>> = HashMap::new();
let mut dummy: Vec<&str> = Vec::new();
let mut ref = map.get_mut("whatever").unwrap_or( &dummy );
// Modify ref.
}

As mentioned by Shepmaster, here is an example of using the entry pattern. It seems verbose at first, but this avoids allocating an array you might not use unless you need it. I'm sure you could make a generic function around this to cut down on the chatter :)
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
fn foo() {
let mut map = HashMap::<&str, Vec<&str>>::new();
let mut result = match map.entry("whatever") {
Vacant(entry) => entry.insert(Vec::new()),
Occupied(entry) => entry.into_mut(),
};
// Do the work
result.push("One thing");
result.push("Then another");
}
This can also be shortened to or_insert as I just discovered!
use std::collections::HashMap;
fn foo() {
let mut map = HashMap::<&str, Vec<&str>>::new();
let mut result = map.entry("whatever").or_insert(Vec::new());
// Do the work
result.push("One thing");
result.push("Then another");
}

If you want to add your dummy into the map, then this is a duplicate of How to properly use HashMap::entry? or Want to add to HashMap using pattern match, get borrow mutable more than once at a time (or any question about the entry API).
If you don't want to add it, then your code is fine, you just need to follow the compiler error messages to fix it. You are trying to use a keyword as an identifier (ref), and you need to get a mutable reference to dummy (& mut dummy):
use std::collections::HashMap;
fn foo() {
let mut map: HashMap<&str, Vec<&str>> = HashMap::new();
let mut dummy: Vec<&str> = Vec::new();
let f = map.get_mut("whatever").unwrap_or( &mut dummy );
}
fn main() {}

Related

Rust multithread intensive methods which need to access same set of data with Rayon

I use Rayons par_iter()to iterate over different variations of the expensive method I need to run. These runs need to access the same set of checked usizes because they all need to add to it and check it from time to time. I also need them all to shutdown when first thread finishes, this is why I have a kill_switch which will force the iterations to exit when its set to true.
let mut checked: HashSet<usize> = HashSet::new();
let mut kill_switch: bool = false;
permutations.par_iter().for_each(|order| {
let board = Board::new(board_map.clone(), order.clone());
let mut bubbles: Vec<(i8, i8)> = Vec::new();
if let Some(bubbles) = board.solve(&mut bubbles, &mut checked, &kill_switch) {
kill_switch = true;
bubbles.into_iter().for_each(|bubble| {
dbg!(bubble);
});
}
})
This is the code I currently have but I get errors for how I'm using checked and kill_switch. How do I make this work?
Errors:
cannot borrow checked as mutable, as it is a captured variable in a Fn closure
cannot borrow as mutable [E0596]
cannot assign to kill_switch, as it is a captured variable in a Fn closure
cannot assign [E0594]
To fix the errors, you will need to use RefCells to wrap the checked and kill_switch variables and use the borrow_mut method to get a mutable reference to them in the closure.
Here is an example of how you can modify your code:
use std::cell::RefCell;
use std::collections::HashSet;
let checked: RefCell<HashSet<usize>> = RefCell::new(HashSet::new());
let kill_switch: RefCell<bool> = RefCell::new(false);
permutations.par_iter().for_each(|order| {
let board = Board::new(board_map.clone(), order.clone());
let mut bubbles: Vec<(i8, i8)> = Vec::new();
if let Some(bubbles) = board.solve(&mut bubbles, &mut checked.borrow_mut(), &mut kill_switch.borrow_mut()) {
*kill_switch.borrow_mut() = true;
bubbles.into_iter().for_each(|bubble| {
dbg!(bubble);
});
}
})
Note that you will also need to add RefCell as a dependency in your project.

Rust peekable double reference

Why does a peekable iterator return a double reference in an Option?
struct Foo {}
let mut foovec = vec![];
foovec.push(Foo {});
let mut iter = foovec.iter().peekable();
let next = iter.peek();
next is an Option<&&Foo>, not an Option<&Foo>.
How do I get it to be the latter?
I do not want to use .into_iter() because I do not want to consume the vec. I just want a borrowed reference to the Foo struct wrapped in an Option.
peek yields references to whatever you're iterating over. If that's also references, it'll yield double references. You can use the copied Option adapter to remove that level of indirection:
struct Foo {}
let mut foovec = vec![];
foovec.push(Foo {});
let mut iter = foovec.iter().peekable();
// use `copied` here to go from Option<&&_> to Option<&_>
let next = iter.peek().copied();
Alternatively, you can just call next after you checked that you want the peeked value:
struct Foo {}
let mut foovec = vec![];
foovec.push(Foo {});
let mut iter = foovec.iter().peekable();
if is_okay(iter.peek()) {
let next = iter.next(); // `next` has type `&Foo`
}
You could even use pattern matching to remove one level of indirection:
if let Some(&next) = iter.peek() {
// `next` has type `&Foo` in this block
}

Sharing a reference in multiple threads inside a function

I want to build a function that takes a HashMap reference as an argument. This HashMap should be shared between threads for read only access. The code example is very simple:
I insert some value into the HashMap, pass it to the function and want antoher thread to read that value. I get an Error that the borrowed value does not live long enough at line let exit_code = test(&m);. Why is this not working?
use std::thread;
use std::collections::HashMap;
use std::sync::{Arc, RwLock };
fn main(){
let mut m: HashMap<u32, f64> = HashMap::new();
m.insert(0, 0.1);
let exit_code = test(&m);
std::process::exit(exit_code);
}
fn test(m: &'static HashMap<u32, f64>) -> i32{
let map_lock = Arc::new(RwLock::new(m));
let read_thread = thread::spawn(move || {
if let Ok(r_guard) = map_lock.read(){
println!("{:?}", r_guard.get(&0).unwrap());
}
});
read_thread.join().unwrap();
return 0;
}
if I don't put the 'static in the function signature for the HashMap argument, Arc::new(RwLock::new(m)); doesn't work. How can I sovlve this problem?
A reference is not safe to share unless is 'static meaning that something will live for the extent of the program. Otherwise the compiler is not able to track the liveliness of the shared element.
You should wrap it outside of the function, and take ownership of an Arc:
use std::thread;
use std::collections::HashMap;
use std::sync::{Arc, RwLock };
fn main(){
let mut map = HashMap::new();
map.insert(0, 0.1);
let m = Arc::new(RwLock::new(map));
let exit_code = test(m);
std::process::exit(exit_code);
}
fn test(map_lock: Arc<RwLock<HashMap<u32, f64>>>) -> i32 {
let read_thread = thread::spawn(move || {
if let Ok(r_guard) = map_lock.read(){
println!("{:?}", r_guard.get(&0).unwrap());
}
});
read_thread.join().unwrap();
return 0;
}
Playground

Multiple Immutable References

I have the following code:
use std::collections::HashMap;
fn doublez(h1: &HashMap<String, i32>, h2: &HashMap<String, i32>) {
dbg!(h1, h2);
}
fn main() {
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
let teams = vec![
String::from("Blue"),
String::from("Yellow"),
];
let initial_scores = vec![10, 50];
let team_scores: HashMap<_, _> = teams.into_iter().zip(initial_scores.into_iter()).collect();
let mut ts2 = &team_scores;
let mut ts3 = &team_scores;
doublez(ts2, ts3);
}
I'm experimenting with Rusts ownership rules and I was testing out the whole idea that you can't have multiple mutable references, but here in this code, I make two mutable references to the team_scores hashmap in the form of ts2 and ts3 but for whatever reason the code compiles just fine. Why is that?
let mut ts2 = &team_scores is not actually creating a mutable reference, but rather a mutable variable containing an immutable reference. This means you can reassign the variable to another reference (i.e. ts2 = &different_hashmap), but you won't be able to modify the HashMap itself (i.e. trying to call ts2.insert will not work).
If you want a mutable reference to the HashMap, you would use let ts2 = &mut team_scores;.

How do I remove `MutexGuard` around a value?

I'm trying to use ndarray as an asynchronous process to do linear algebra and such.
I used Rust's tokio and ndarray to create the following code.
use std::sync::{Arc, Mutex};
use ndarray::prelude::*;
use futures::future::join_all;
fn print_type_of<T>(_: &T) {
println!("{}", std::any::type_name::<T>())
}
#[tokio::main]
async fn main() {
let db = Arc::new(Mutex::new(array![0,0,0,0,0,0,0,0]));
let mut handels = vec![];
for i in 0..8 {
let db = db.clone();
let unchange_array = unchange_array.clone();
handels.push(tokio::spawn(async move{
print(i, db).await;
}));
}
join_all(handels).await;
let array = Arc::try_unwrap(db).unwrap();
let array = array.lock().unwrap();
print_type_of(&array); // -> std::sync::mutex::MutexGuard<ndarray::ArrayBase<ndarray::data_repr::OwnedRepr<u32>, ndarray::dimension::dim::Dim<[usize; 1]>>>
}
async fn print(i: u32, db: Arc<Mutex<Array1<u32>>>) {
let unchange = unchange.to_owned();
let mut tmp = 0;
// time-consuming process
for k in 0..100000000 {
tmp = k;
}
tmp += i;
let mut db = db.lock().unwrap();
db.fill(i);
println!("{:?}", unchange);
print_type_of(&db);
}
I would like to change the data std::sync::mutex::MutexGuard<ndarray::ArrayBase<OwnedRepr<u32>, Dim<[usize; 1]>>>
to ndarray::ArrayBase<OwnedRepr<u32>, Dim<[usize; 1]>>.
How can I do this?
You can't. That's the whole point of MutexGuard: if you could take the data out of the MutexGuard, then you would be able to make a reference that can be accessed without locking the mutex, defeating the whole purpose of having a mutex in the first place.
Depending on what you really want to do, one of the following solutions might apply to you:
Most of the time, you don't need to take the data out of the mutex: MutexGuard<T> implements Deref<Target=T> and DerefMut<Target=T>, so you can use the MutexGuard everywhere you would use a &T or a &mut T. Note that if you change your code to call print_type_of(&*array) instead of print_type_of(&array), it will print the inner type.
If you really need to, you can take the data out of the Mutex itself (but not the MutexGuard) with into_inner, which consumes the mutex, ensuring that no one else can ever access it:
let array = Arc::try_unwrap(db).unwrap();
let array = array.into_inner().unwrap();
print_type_of(&array); // -> ndarray::ArrayBase<ndarray::data_repr::OwnedRepr<u32>, ndarray::dimension::dim::Dim<[usize; 1]>>

Resources