3

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

Rohit Sharma
  • 6,136
  • 3
  • 28
  • 47

2 Answers2

5

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

Jmb
  • 18,893
  • 2
  • 28
  • 55
4

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:

E_net4
  • 27,810
  • 13
  • 101
  • 139