Unnecessary mut lint does not catch some cases where &mut works

(Sorry if there was a better category to post this under)

The following code compiles and runs without warning, whether or not we use let mut = or = &mut for the iterator creation:

fn main() {
  let nums = vec![2, 4, 6, 8];
  let mut num_iter = nums.iter();
  // let num_iter = &mut nums.iter();
  let n1 = num_iter.next();
  println!("first num is {:?}", n1)
}

Since it seems to be that the former (let mut) is a bit of a code smell when the latter (&mut) would work in its stead, would it make sense to have a (possibly optional) lint for this case?

The former is not a code smell and the types between both variants are different. The first is vec::Iter<_>, the second is &mut vec::Iter<_> (a reference to an iterator, which, by itself, references the vec again). While both work (through the . operator dereferencing and such), the second is definitely not the code generally in use.

2 Likes

Thanks for the explanation. I haven't used Rust enough to know what is generally in use, but the first case seems to allow more mutability than the latter; either type works as you point out in this case. So it seems that using the "principle of least privilege" would suggest it would be better to use &mut when possible. Though, I suppose the dereference could have a performance impact in some situations.

Performance is not really the concern there, I would expect the compiler to see through this. But the two types are semantically very different. The one is the bare type and the other is the mutable reference, which follow different rules. "More mutability" is not really at play here, even if both have different rulesets (the first follows Ownership, the second the referencing rules).

Rust is not really about constraining mutability in general anyways - it's about constraining shared mutability, which both effectively constrain. A mutable reference is in play anyways - the signature of next is fn next(&mut self), so the iterator will become referenced at that moment. Especially iterators need to always be mutably accessed (because you advance their state!). Note though that mutable access to the iterator (do advance it) does not mean mutable access to the collection (that's why iter_mut() also exists)!

Thanks for raising the question, though!

2 Likes

These sorts of questions about Rust should be asked on users.rust-lang.org. internals.rust-lang.org is meant for discussions about the compiler, surrounding tooling, and RFCs.