Branching a iterator without deep copying its elements - rust

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.

Related

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;.

Equivalent of split_at_mut for HashMap?

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)
}

In Rust, how do I create a HashSet from the keys of a HashMap?

I have two HashMaps and want to compute the intersection of the keys. Is it possible to construct a HashSet out of whatever HashMap::keys() returns? For example:
use std::collections::{HashSet, HashMap};
fn main() {
let mut map1: HashMap<i64, i64> = HashMap::new();
let mut map2: HashMap<i64, i64> = HashMap::new();
// Add some values into the HashMaps for demonstration
map1.insert(1, 10);
map1.insert(5, 50);
map2.insert(3, 30);
map2.insert(5, 50);
let set1: HashSet<i64> = HashSet::from(map1.keys()); // How to do this?
let set2: HashSet<i64> = HashSet::from(map2.keys()); // How to do this?
let set3 = set1.intersection(&set2); // What I'm looking to accomplish
// set3 should contain [5], as this is the one key shared by the two HashMaps
}
The simple solution
Your code only needs a few tweaks to actually compile (see Playground):
use std::collections::{HashSet, HashMap};
fn main() {
let mut map1 = HashMap::new();
let mut map2 = HashMap::new();
// Add some values into the HashMaps for demonstration
map1.insert(1, 10);
map1.insert(5, 50);
map2.insert(3, 30);
map2.insert(5, 50);
let set1: HashSet<i64> = map1.keys().cloned().collect();
let set2: HashSet<i64> = map2.keys().cloned().collect();
let set3 = set1.intersection(&set2);
println!("{:?}", set3);
}
In particular, note map1.keys().cloned().collect():
HashMap<K, V>::keys() returns an Iterator<Item = &'a K>,
.cloned() transforms that to an Iterator<Item = K>,
.collect() builds a collection from that, since HashSet implements the FromIterator trait.
However, this is not very efficient:
Complexity wise: O(map1.size() + map2.size()).
Memory wise: potentially large allocations.
The efficient solution
Implement intersection directly on the keys of HashMap.
You just need to collect into the HashSet:
let set1: HashSet<i64> = map1.keys().copied().collect();
let set2: HashSet<i64> = map2.keys().copied().collect();
Using copied() will dereference the keys and copy them, since you want a HashSet<i64> not a HashSet<&i64>

Mutate vector within filter

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);
}

Updating mutable HashMap in a while loop

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]}

Resources