Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Iterator::all need the iterator to be mutable?

Tags:

iterator

rust

I was using an iterator on a String's characters:

pub fn is_yelling(message: &str) -> bool {
    let letters = message.chars().filter(|c| c.is_alphabetic());
    message.chars().any(|c| c.is_alphabetic()) && letters.all(|c| c.is_uppercase())
}

This raises a compilation error:

error[E0596]: cannot borrow immutable local variable `letters` as mutable
 --> src/main.rs:3:51
  |
2 |     let letters = message.chars().filter(|c| c.is_alphabetic());
  |         ------- consider changing this to `mut letters`
3 |     message.chars().any(|c| c.is_alphabetic()) && letters.all(|c| c.is_uppercase())
  |                                                   ^^^^^^^ cannot borrow mutably

When I make letters mutable everything runs smoothly.

I do not understand why this necessary. The all method should not have to alter the the iterator. Like map or filter, which take self and not mut self as an argument.

My reference for map / filter / all.

I saw an issue on the matter, but no explanation was given.

like image 939
Justmaker Avatar asked May 14 '26 00:05

Justmaker


2 Answers

Iterating anything requires mutating the iterator because Iterator::next takes &mut self. Checking all the values in the iterator requires iteration, therefore Iterator::all (and many similar methods) requires &mut self as well.

The all method should not have to alter the the iterator.

I am very interested to hear how you propose to check every value in an iterator without calling next.

Like map or filter

These methods return a new iterator, they do not call next. That being said, they could if they wanted because...

which take self and not mut self as an argument.

Mutability is a property of the owner of a variable. These two functions are equivalent:

fn example(mut a: String) {}
fn example(a: String) {
    let mut a = a;
}

Importantly, both look the same in the generated documentation — neither have mut in the signature. This is because it doesn't matter to the caller.

like image 194
Shepmaster Avatar answered May 15 '26 19:05

Shepmaster


The iterator methods all and any take a mutable reference because they will be modifying the iterator by consuming its elements (note that next() also requires &mut self, since it inherently modifies the state of the iterator). In addition, the methods are short-circuiting, and do not necessarily consume all elements of the iterator. This means that the methods can give the iterator back to the caller, thus being able to continue consuming from it if so is desired.

let x = vec![1, 2, 5];
let mut it = x.into_iter();
assert!(it.any(|x| x > 1));
assert_eq!(it.next(), Some(5));

map or filter work differently, because they are iterator adaptors. Once invoked, the returned adaptor value will own the underlying iterator, and thus requires self to be moved. Values do not have to be bound to a mutable variable to be moved to another scope, even if they are modified later on in that context.

like image 20
E_net4 stands with Ukraine Avatar answered May 15 '26 19:05

E_net4 stands with Ukraine



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!