Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Modifying self in `iter_mut().map(..)`, aka mutable functional collection operations

Tags:

iterator

rust

How do I convert something like this:

let mut a = vec![1, 2, 3, 4i32];
for i in a.iter_mut() {
    *i += 1;
}

to a one line operation using map and a closure?

I tried:

a.iter_mut().map(|i| *i + 1).collect::<Vec<i32>>();

The above only works if I reassign it to a. Why is this? Is map getting a copy of a instead of a mutable reference? If so, how can I get a mutable reference?

like image 362
abject_error Avatar asked Feb 21 '15 20:02

abject_error


1 Answers

Your code dereferences the variable (*i) then adds one to it. Nowhere in there does the original value get changed.

The best way to do what you asked is to use Iterator::for_each:

a.iter_mut().for_each(|i| *i += 1);

This gets an iterator of mutable references to the numbers in your vector. For each item, it dereferences the reference and then increments it.

You could use map and collect, but doing so is non-idiomatic and potentially wasteful. This uses map for the side-effect of mutating the original value. The "return value" of assignment is the unit type () - an empty tuple. We use collect::<Vec<()>> to force the Iterator adapter to iterate. This last bit ::<...> is called the turbofish and allows us to provide a type parameter to the collect call, informing it what type to use, as nothing else would constrain the return type.:

let _ = a.iter_mut().map(|i| *i += 1).collect::<Vec<()>>();

You could also use something like Iterator::count, which is lighter than creating a Vec, but still ultimately unneeded:

a.iter_mut().map(|i| *i += 1).count();

As Ry- says, using a for loop is more idiomatic:

for i in &mut a {
    *i += 1;
}
like image 186
Shepmaster Avatar answered Oct 25 '22 19:10

Shepmaster