I am trying to implement Karger's algorithm in Rust and I have run into an issue while trying to update a mutable hashmap in a while loop.
The map is updated successfully, but then in the next iteration when it was cloned, the values that were updated don't seem to have been changed. However removing elements from the map is reflected on later iterations.
I have tried debugging and printing the values of the map, but the sequence of events doesn't make sense to me.
use itertools::Itertools; // 0.8.0
use rand::seq::{IteratorRandom, SliceRandom}; // 0.6.5
use std::collections::HashMap;
fn contract_edge(graph: &mut HashMap<i32, Vec<i32>>, num_trials: i32) {
let mut count = 0;
while graph.len() > 2 && count < num_trials {
// clone graph so I can mutate graph later
let imut_graph = graph.clone();
// choose random node
let from_value = imut_graph
.keys()
.choose(&mut rand::thread_rng())
.unwrap()
.clone();
let values = imut_graph.get(&from_value);
let to_value = values
.unwrap()
.choose(&mut rand::thread_rng())
.unwrap()
.clone();
let from_edges = imut_graph[&from_value].iter().clone();
// accessing to_value in imut_graph gives error here later
let to_edges = imut_graph[&to_value]
.iter()
.clone()
.filter(|&x| *x != from_value && *x != to_value);
let new_edges = from_edges.chain(to_edges);
// since I am mutating the graph I thought the next time is is clone it would be updated?
graph.insert(from_value, new_edges.map(|v| v.clone()).collect());
graph.remove(&to_value);
for (_key, val) in graph.iter_mut() {
*val = val
.iter()
.map(|v| if v == &to_value { &from_value } else { v })
.unique()
.cloned()
.collect();
}
count += 1;
}
}
When I try to access the map, I get element not found error, but the keys which have been removed should not exist in the vector values at that point.
I am convinced this is something I don't understand about (Im)mutability in Rust.
I'm not really sure what you are trying to achieve here, but based on what I can see above, that is, you'd like to mutate your original graph (because you are passing that as a mutable borrow to your function) and that you don't have a return value, and that your question is about mutating a hashmap -- I assume that you'd like the changes to be reflected in your original HashMap. So why are you cloning it in the first place then?
If on the other hand you don't want to mutate your original object, then don't pass it in as a mutable borrow, but as an immutable one. Then create a clone of it before you start the loop and use that cloned version throughout your algorithm.
The problem you are facing with is happening because on every iteration you are cloning the original graph and not your cloned imut_graph, i.e. on every iteration you create a new HashMap, which then you are mutating, then you start a new cycle and you are still checking the length of the original one and then you clone the original one again.
So the two options you have are:
use std::collections::HashMap;
fn mutated(map: &mut HashMap<i32, Vec<i32>>) {
map.insert(1, vec![4, 5, 6]);
}
fn cloned(map: &HashMap<i32, Vec<i32>>) -> HashMap<i32, Vec<i32>> {
let mut map = map.clone();
map.insert(2, vec![7, 8, 9]);
map
}
fn main() {
let mut map = HashMap::new();
map.insert(0, vec![1, 2, 3]);
println!("{:?}", cloned(&map));
mutated(&mut map);
println!("{:?}", map);
}
Which will give you:
{0: [1, 2, 3], 2: [7, 8, 9]}
{0: [1, 2, 3], 1: [4, 5, 6]}
Related
I am learning rust, Run the following code.
I want to change each value of the array
use rand::Rng;
// fn main() {
// let mut x: [i32; 30] = [1; 30];
// for (index, value) in x.iter().enumerate() {
// // no worker
// x[index] = rand::thread_rng().gen_range(1..100);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `x[_]` occurs here
// println!("{}", x[index]);
// }
// }
fn main() {
let mut x = [1; 30];
let enumerate = x.iter().enumerate();
for index in 0..x.len() {
// worker!
x[index] = rand::thread_rng().gen_range(1..100);
println!("{}", x[index]);
}
}
I want to know why
The error is happening because the code is trying to modify an array while it's being used by an iterator. To fix this, you need to use an iterator that allows you to modify the array.
In Rust, there's a distinction between iter() and iter_mut(). iter() returns an iterator that gives you read-only access to the elements of an array, while iter_mut() returns an iterator that gives you mutable access to the elements of an array.
So, in your code, instead of using x.iter().enumerate(), you can use x.iter_mut().enumerate() to get an iterator that lets you modify the elements of x:
use rand::Rng;
fn main() {
let mut x: [i32; 30] = [1; 30];
for (_, value) in x.iter_mut().enumerate() {
*value = rand::thread_rng().gen_range(1..100);
println!("{}", *value);
}
}
Iterators is consumed by map, so require cloned(equals to clone?) when mapping a iterator multiple times. I have following code:
fn main() {
let a: Vec<(&str, (&i32, &i32))> = vec![("0", (&0, &1)), ("1", (&2, &3))];
let iter = a.iter().map(|x| x.1 as (&i32, &i32));
// occur clone process on i32?
let xs: Vec<&i32> = iter.cloned().map(|(x, _)| x).collect();
let ys: Vec<&i32> = iter.map(|(_, y)| y).collect();
println!("{:?}", xs);
println!("{:?}", ys);
}
The problem is that cloned method probably deep copy whole an iterated element even though the elements contain immutable references. Cloned elements is i32 in above, but it may actually have big structure. I want to explicitly shallow copy elements which contain references to i32 (actually bigger struct) when branch iterator iter. Is it possible?
Use clone instead of cloned:
fn main() {
let a: Vec<(&str, (&i32, &i32))> = vec![("0", (&0, &1)), ("1", (&2, &3))];
let iter = a.iter().map(|x| x.1 as (&i32, &i32));
// occur clone process on i32?
let xs: Vec<&i32> = iter.clone().map(|(x, _)| x).collect();
let ys: Vec<&i32> = iter.map(|(_, y)| y).collect();
println!("{:?}", xs);
println!("{:?}", ys);
}
Playground
clone clones the iterator itself. Meanwhile cloned clones the underlying elements, from the documentation:
Cloned: An iterator that clones the elements of an underlying iterator.
Consider the following code (minimum example):
use std::collections::HashMap;
fn main() {
let mut map: HashMap<usize, i128> = HashMap::new();
map.insert(1, -5);
map.insert(2, 6);
map.insert(3, 7);
for i in map.keys() {
if *i == 3 {
continue;
}
*map.get_mut(&3).unwrap() += map[i];
}
}
The borrow checker will complain that:
cannot borrow `map` as mutable because it is also borrowed as immutable
However, in this case I can be sure that the mutation I am doing is not interfering with the immutable references. For Vec, I would use split_at_mut here - is there an equivalent for that for HashMap in Rust?
Edit:
As a comment pointed out, let me be more specific about the problem I am trying to solve. I want to implement "merging" vertices in a graph into one vertex. For this I created a HashMap:
pub mergedVerticesList: HashMap<usize, HashSet<usize>>
which should map from a vertex to all the vertices that have been merged into that vertex. Since this can be done recursively, when undoing a merge between u and merge_onto I want to remove all vertices that have been merged into merge_onto because of u from merge_onto's HashSet. Thus, the code looks like this:
for i in self.mergedVerticesList[v].iter() {
self.mergedVerticesList.get_mut(&merge_onto).unwrap().remove(i);
}
No, this is not possible as far as the HashMap is concerned. You could have cells as values to regain mutable access but I'd strongly advise against that. Can't you move the change out of the loop (it also saves from a repeated lookup per iteration). Something to the tune of
use std::collections::HashMap;
fn main() {
let mut map: HashMap<usize, i128> = HashMap::new();
map.insert(1, -5);
map.insert(2, 6);
map.insert(3, 7);
let s = map.iter().filter_map(|(k, v)| if *k != 3 { Some(v) } else { None }).sum::<i128>();
*map.get_mut(&3).unwrap() += s;
println!("{:#?}", &map)
}
So, I have the following code successfully performing filter in vector:
let mut v1 : Vec<i32> = vec!(1,2,3);
let v2 : Vec<&mut i32> = v1.iter_mut().filter(|x| {**x == 2}).collect();
println!("{:?}", v2);
Since the type signature of the predicate in the filter function is
FnMut(&Self::Item) -> bool, I was assuming that that mutation inside
the closure will work:
let mut v1 : Vec<i32> = vec!(1,2,3);
let v2 : Vec<&mut i32> = v1.iter_mut().filter(|x| {**x = 3; **x == 2}).collect();
println!("{:?}", v2);
But the above code results in a compile error. How to fix that ? Note
that I'm playing with rust to get a better understanding, so the abpve
example doesn't make sense (usually, nobody will try to mutate
things inside filter).
You are confusing two concepts: FnMut means that a function can change its captured variables, like:
fn main() {
let v1 = vec![1, 2, 3];
let mut i = 0usize;
let v2: Vec<_> = v1
.into_iter()
.filter(|x| {
i = i + 1;
*x == 2
})
.collect();
println!("We iterate {} times and produce {:?}", i, v2);
}
This doesn't mean that every parameter of a function will be mutable.
In your code, filter() takes a &Self::Item, which is very different from the map() one that takes Self::Item. Because the real type will translate to Map<Item=&mut i32> and Filter<Item=&&mut i32>. Rust forbids you from mutating a reference if it's behind a non mutable reference:
fn test(a: &&mut i32) {
**a = 5;
}
error[E0594]: cannot assign to `**a` which is behind a `&` reference
This is because Rust follows the the-rules-of-references:
At any given time, you can have either one mutable reference or any number of immutable references.
References must always be valid.
This means you can have more than one &&mut but only one &mut &mut. If Rust didn't stop you, you could mutate a &&mut and that would poison any other &&mut.
Unfortunately the full error description of E0594 is still not available, see #61137.
Note: Avoid side effects when you use the iterator API, I think it's OK to mutate your FnMut state but not the item, you should do this in a for loop, like:
fn main() {
let mut v1 = vec![1, 2, 3];
for x in v1.iter_mut().filter(|x| **x == 2) {
*x = 1;
}
println!("{:?}", v1);
}
I have a vector of sets and I want to remove all sets that are subsets of other sets in the vector. Example:
a = {0, 3, 5}
b = {0, 5}
c = {0, 2, 3}
In this case I would like to remove b, because it's a subset of a. I'm fine with using a "dumb" n² algorithm.
Sadly, it's pretty tricky to get it working with the borrow checker. The best I've come up with is (Playground):
let mut v: Vec<HashSet<u8>> = vec![];
let mut to_delete = Vec::new();
for (i, set_a) in v.iter().enumerate().rev() {
for set_b in &v[..i] {
if set_a.is_subset(&set_b) {
to_delete.push(i);
break;
}
}
}
for i in to_delete {
v.swap_remove(i);
}
(note: the code above is not correct! See comments for further details)
I see a few disadvantages:
I need an additional vector with additional allocations
Maybe there are more efficient ways than calling swap_remove often
If I need to preserve order, I can't use swap_remove, but have to use remove which is slow
Is there a better way to do this? I'm not just asking about my use case, but about the general case as it's described in the title.
Here is a solution that does not make additional allocations and preserves the order:
fn product_retain<T, F>(v: &mut Vec<T>, mut pred: F)
where F: FnMut(&T, &T) -> bool
{
let mut j = 0;
for i in 0..v.len() {
// invariants:
// items v[0..j] will be kept
// items v[j..i] will be removed
if (0..j).chain(i + 1..v.len()).all(|a| pred(&v[i], &v[a])) {
v.swap(i, j);
j += 1;
}
}
v.truncate(j);
}
fn main() {
// test with a simpler example
// unique elements
let mut v = vec![1, 2, 3];
product_retain(&mut v, |a, b| a != b);
assert_eq!(vec![1, 2, 3], v);
let mut v = vec![1, 3, 2, 4, 5, 1, 2, 4];
product_retain(&mut v, |a, b| a != b);
assert_eq!(vec![3, 5, 1, 2, 4], v);
}
This is a kind of partition algorithm. The elements in the first partition will be kept and in the second partition will be removed.
You can use a while loop instead of the for:
use std::collections::HashSet;
fn main() {
let arr: &[&[u8]] = &[
&[3],
&[1,2,3],
&[1,3],
&[1,4],
&[2,3]
];
let mut v:Vec<HashSet<u8>> = arr.iter()
.map(|x| x.iter().cloned().collect())
.collect();
let mut pos = 0;
while pos < v.len() {
let is_sub = v[pos+1..].iter().any(|x| v[pos].is_subset(x))
|| v[..pos].iter().any(|x| v[pos].is_subset(x));
if is_sub {
v.swap_remove(pos);
} else {
pos+=1;
}
}
println!("{:?}", v);
}
There are no additional allocations.
To avoid using remove and swap_remove, you can change the type of vector to Vec<Option<HashSet<u8>>>:
use std::collections::HashSet;
fn main() {
let arr: &[&[u8]] = &[
&[3],
&[1,2,3],
&[1,3],
&[1,4],
&[2,3]
];
let mut v:Vec<Option<HashSet<u8>>> = arr.iter()
.map(|x| Some(x.iter().cloned().collect()))
.collect();
for pos in 0..v.len(){
let is_sub = match v[pos].as_ref() {
Some(chk) =>
v[..pos].iter().flat_map(|x| x).any(|x| chk.is_subset(x))
|| v[pos+1..].iter().flat_map(|x| x).any(|x| chk.is_subset(x)),
None => false,
};
if is_sub { v[pos]=None };//Replace with None instead remove
}
println!("{:?}", v);//[None, Some({3, 2, 1}), None, Some({1, 4}), None]
}
I need an additional vector with additional allocations
I wouldn't worry about that allocation, since the memory and runtime footprint of that allocation will be really small compared to the rest of your algorithm.
Maybe there are more efficient ways than calling swap_remove often.
If I need to preserve order, I can't use swap_remove, but have to use remove which is slow
I'd change to_delete from Vec<usize> to Vec<bool> and just mark whether a particular hashmap should be removed. You can then use the Vec::retain, which conditionaly removes elements while preserving order. Unfortunately, this function doesn't pass the index to the closure, so we have to create a workaround (playground):
let mut to_delete = vec![false; v.len()];
for (i, set_a) in v.iter().enumerate().rev() {
for set_b in &v[..i] {
if set_a.is_subset(&set_b) {
to_delete[i] = true;
}
}
}
{
// This assumes that retain checks the elements in the order.
let mut i = 0;
v.retain(|_| {
let ret = !to_delete[i];
i += 1;
ret
});
}
If your hashmap has a special value which can never occur under normal conditions, you can use it to mark a hashmap as "to delete", and then check that condition in retain (it would require changing the outer loop from iterator-based to range-based though).
Sidenote (if that HashSet<u8> is not just a toy example): More eficient way to store and compare sets of small integers would be to use a bitset.