Search code examples
rustreferencefold

fold is picky about closures it accepts


(rust noob here; I'm trying to understand what can/cannot/should/shouldn't be passed by reference in a higher order function situation)

let a = [1, 2, 3];

This invocation works:

let sum = a.iter().fold(0, |acc:  i32, x: &i32| acc + x);

These do not:

let sum = a.iter().fold(0, |acc: i32, x: i32| acc + x);
let sum = a.iter().fold(0, |acc: &i32, x: i32| acc + x);

The error message is

error[E0631]: type mismatch in closure arguments
 --> main.rs:8:22
  |
8 |   let sum = a.iter().fold(0, |acc: &i32, x: i32| acc + x);
  |                      ^^^^    --------------------------- found signature of `for<'r> fn(&'r i32, i32) -> _`
  |                      |
  |                      expected signature of `fn({integer}, &{integer}) -> _`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0631`.

The explanation does not offer anything of interest. It says the arguments of the closure do not match those of the arguments of fold. I however cannot see how it follows from the declaration of fold:

fn fold<B, F>(self, init: B, f: F) -> B
where
    F: FnMut(B, Self::Item) -> B

Why is the second argument supposed to be &{integer}, and the first one {integer}?


Solution

  • The items in the iterator are borrowed from the array, so are &i32 not i32. This form works because the accumulator is owned, while the items are borrowed:

    let sum = a.iter().fold(0, |acc: i32, x: &i32| acc + x);
    

    You can convert the iterator so its items are copied instead of referenced, and then the first form will work:

    let sum = a.iter().copied().fold(0, |acc: i32, x: i32| acc + x);
    

    The third form can never work. The closure needs to be able to return a new value with which to update the accumulator. The type of the accumulator is i32. It can't be a reference because you can't return a reference from a closure (the original value would be dropped and Rust won't let you return a dangling pointer).