What is the difference between b.iter().map(|x| {}) and for x in b.iter() {}

The title says it mostly: What is the difference (in performance) between:

let b = [1, 3, 4, 1, 5, 2, 6, 2];

let mut result = 0;
for x in b.iter() {
   result += x; 
}

and

let b = [1, 3, 4, 1, 5, 2, 6, 2];

let mut result = 0;
b.iter().map(|x| result += *x);

.
Or is the only difference syntaxtual?

The major difference is explained by the warning you get in the latter:

  = note: `#[warn(unused_must_use)]` on by default
  = note: iterators are lazy and do nothing unless consumed

the second one doesn't actually do anything, but the first does.

Other than that, they're the same, yes.

1 Like

To add to that,

b.iter().for_each(|x| result += *x);

is the actual equivalent of the for loop (i.e. it actually runs through the iterator).

1 Like

The technical jargon for .for_each() and for x in y iteration is Internal Iterator and External Iterator (wikipedia).

In the simple case they'll compile down to pretty much the same machine code. I remember reading an email from early on in Rust's development where they only had internal iterators, and apparently LLVM was able to generate better machine code.

Iterators using callback closures are more restrictive about borrow checking. For example:

b.iter().inspect(|_| result += 1).for_each(|x| result += *x);

won't compile, because only one closure can borrow result as mutable. When the code is outside closures in regular flow of a function, the borrow checker can analyze it better and allow more.

A third possibility which uses an iterator but avoids the use of a borrowed variable in the closure is to use fold:

    let result = b.iter().fold(0, |acc, x| acc + x);

I'd expect all three to have the same performance.

1 Like