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);
}
Related
Since Iter's "all" fn takes type FnMut is it possible to update the element while checking for condition and short circuiting? Though I understand it is not supposed to, but what prevents it from updating the value?
fn main() {
let a = ["as", "zz"];
let mut iter = a.iter();
iter.all(|& (mut x)| {x = "cc"; true});
for z in a.iter(){
println!("{z}");
}
}
Above prints
as
zz
In above case why setting "x = cc" not work?
Or Alternatively why does Iter "all" method takes F of type FnMut and not Fn when it is not supposed to mutate but only validate for condition
x = "cc" does not change the value referred by x, instead it changes the reference itself (i.e. it makes it refer to another value), as evidenced by this example:
fn main() {
let a = ["as", "zz"];
let mut iter = a.iter();
iter.all(|&(mut x)| {
println!("before: {:?}", x as *const _);
x = "cc";
println!("after: {:?}", x as *const _);
true});
for z in a.iter(){
println!("{z}");
}
}
Playground
Note that this has nothing to do with the fact that the closure is FnMut, which only means that it may change any captured values as in:
fn main() {
let a = ["as", "zz"];
let mut count = 0;
let mut iter = a.iter();
iter.all(|&_x| {
count += 1; // This is possible because the closure is `FnMut`
true});
println!("Count: {count}");
for z in a.iter(){
println!("{z}");
}
}
Playground
Do not be mistaken! A function implementing FnMut means that it captures a receiving context mutably. It does not mean that its items may be modified from its original source.
let mut k = 0;
assert_eq!((10..15)..all(|x| {
k += 1;
x > k
}), true);
Given
let mut iter = a.iter();
we have an iterator the items of which are references to elements in vector a. And these references are immutable. In order to have an iterator which allows you to mutate the items in a vector, use iter_mut.
let iter = a.mut_iter();
iter.all(|x| { *x = "cc"; true});
for z in a.iter(){
println!("{z}");
}
This would still not require FnMut in particular, since it does not capture any context other than the items iterated on. The adaptor all (and many other iterator adaptors) were designed to constrain to FnMut so that the closures passed as their first parameter are allowed to keep and manipulate some external state for whichever logic they intend to do. Since the closure is called sequentially in the same thread, this is always memory safe.
See also:
What's the difference between placing "mut" before a variable name and after the ":"?
When does a closure implement Fn, FnMut and FnOnce?
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;.
I am still learning Rust and when trying to implement Dikjstra as part of a training project, I encountered this peculiar catch. First I define a HashMap:
let mut dist: HashMap<Node, usize> = HashMap::new();
And later:
let state = State { node: next_node.clone(), cost: cost + 1 };
let current_dist = dist.get(&state.node);
if (current_dist == None) || (state.cost < *current_dist.unwrap()) {
dist.insert(state.node.clone(), state.cost);
heap.push(state);
}
Which yields a compile error because dist.get triggers a immutable borrow which stays in scope until after the if ... {...} statement, and in particular when I dist.insert, asking for a mutable borrow.
I think I miss a pattern or a keyword allowing me this type of process. For now I tried a drop at the beginning of the if scope, and other current_dist evaluation such as
let current_dist;
{
current_dist = dist.get(&state.node);
}
or
let current_dist = {|| dist.get(&state.node)}();
but the end of scope of the immutable borrow still happen after the if statement.
After non-lexical lifetimes
Since non-lexical lifetimes are now enabled, the original code compiles. That being said, you should still use the entry API for efficiency, otherwise you have to hash the key multiple times:
use std::collections::hash_map::Entry;
use std::collections::HashMap;
fn main() {
let mut dist: HashMap<u8, u8> = HashMap::new();
let cost = 21;
match dist.entry(42) {
Entry::Vacant(entry) => {
entry.insert(42);
}
Entry::Occupied(mut entry) => {
if *entry.get() < cost {
entry.insert(42);
}
}
}
}
Before non-lexical lifetimes
because dist.get triggers a mutable borrow
No, it's just an immutable borrow:
pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V>
where
K: Borrow<Q>,
Q: Hash + Eq,
I tried a drop
Explicit drops do not affect lifetimes.
let current_dist;
{
current_dist = dist.get(&state.node);
}
Here you aren't fooling anyone. If the compiler was confused by this, it wouldn't be very good. This still has a borrow to the HashMap, there's just some extra blocks scattered about.
let current_dist = {|| dist.get(&state.node)}();
Same here. Returning the reference from a closure is still returning a reference. You really cannot easily trick the compiler into thinking that your reference to the HashMap doesn't exist.
You need to use a block to constrain how long the borrow exists. the simplest transformation is something akin to:
use std::collections::HashMap;
fn main() {
let mut dist: HashMap<u8, u8> = HashMap::new();
let do_it = {
let current_dist = dist.get(&42);
current_dist == None || true
};
if do_it {
dist.insert(42, 42);
}
}
This isn't the prettiest, but some combinators can clean it up:
use std::collections::HashMap;
fn main() {
let mut dist: HashMap<u8, u8> = HashMap::new();
let cost = 21;
if dist.get(&42).map_or(true, |&val| val < cost) {
dist.insert(42, 42);
}
}
Note that now there's no more implicit panic from the unwrap call.
See also:
How to update-or-insert on a Vec?
As far as I know, the & symbol creates a reference. But using sum_vec with or without & will both compile. I just want to know what is happening when I do let s1 = sum_vec(&v1);. will this create a reference of a reference ?
fn main() {
// Don't worry if you don't understand how `fold` works, the point here is that an immutable reference is borrowed.
fn sum_vec(v: &Vec<i32>) -> i32 {
return v.iter().fold(0, |a, &b| a + b);
}
// Borrow two vectors and sum them.
// This kind of borrowing does not allow mutation to the borrowed.
fn foo(v1: &Vec<i32>, v2: &Vec<i32>) -> i32 {
// do stuff with v1 and v2
let s1 = sum_vec(v1);//This wil also complile with &. Is this optional?.
let s2 = sum_vec(v2);
// return the answer
s1 + s2
}
let v1 = vec![1, 2, 3];
let v2 = vec![4, 5, 6];
let answer = foo(&v1, &v2);
println!("{}", answer);
println!("{}", v1.len());
}
(playground)
Yes and No. Rust will create a reference to a reference (since you asked explicitly for it with the & operator), and then immediately "autoderef" it again to fit the target type. The optimizer will then eliminate that intermediate reference.
Take this example:
fn main() {
let dato = std::sync::Arc::new(std::sync::Mutex::new(1u8));
for _ in 0..3 {
let value = dato.clone();
std::thread::spawn(move || {
let v = value.lock().unwrap();
*v += 1; // <- Error
});
}
std::thread::sleep(std::time::Duration::from_secs(1u64));
println!("{:?}", dato);
}
cannot borrow immutable local variable v as mutable
I know that changing to mut works:
std::thread::spawn(move || {
let mut v = value.lock().unwrap();
*v += 1;
});
but why does this work:
let value = dato.clone();
std::thread::spawn(move || {
*value.lock().unwrap() += 1;
});
playground
value.lock().unwrap() returns a value of type MutexGuard, which has a DerefMut implementation:
impl<'mutex, T: ?Sized> DerefMut for MutexGuard<'mutex, T> {
fn deref_mut(&mut self) -> &mut T { ... }
}
DerefMut::deref_mut(x) is equivalent to &mut *x; naturally, DerefMut is also used for assignments under the pointer, like in your case.
Therefore, for *v += 1 to work, v should be a mut variable - otherwise it would be impossible for DerefMut::deref_mut to be invoked at all.
*value.lock().unwrap() += 1 works because now value.lock().unwrap() is a temporary variable without an explicit binding, so Rust is free to assign its mutability automatically.
The fact that Mutex contains an UnsafeCell inside is not related to this particular thing about DerefMut directly; however, it does mean that Mutex provides something called internal mutability, i.e. it allows one to mutate its contents through a shared reference. You can read more on it in the book.