For context, let's discuss the difference between two things in Rust which look similar, but are quite different.
&T
and &mut T
are two distinct types with very different semantics. &T
is a shared reference which you can copy, whereas &mut T
is an exclusive reference which you can't copy (but you can reborrow). It's undefined behavior to have a &mut T
and a &T
be active at the same time while pointing to the same place, because the &mut T
is exclusive -- that's how deeply ingrained the exclusivity of &mut _
is for the language.
In contrast, let a: T = ...
and let mut b: T ...
does not result in a
and b
having different types; they're both T
. We say a
and b
are bindings; you can have mut
or non-mut
bindings. Non-mut
bindings restrict what you can do with the variable. Namely, you can't overwrite it, and you can't create a &mut _
to it. If you take a compiling program and change every non-mut
binding to a mut
binding, you won't change the semantics of your program. You can think of non-mut
bindings as a compiler-enforced lint.
Whether a binding is mut
or not doesn't change your ability to move values either. For example, you can move a variable between mut
and non-mut
bindings.
let s1 = "Hello".to_string();
let mut s2 = s1;
s2.push_str(", world!");
So non-mut
bindings do not mean "everything about this variable is intrinsically immutable". You can move it to a new mut
binding, and there are other ways non-mut
bindings can be involved with mutations (e.g. a non-mut
binding of a &mut _
reference, atomics and other interior mutability types).
Alright, with that context, let's look at your code.
// takes a takes an
// shared ref exclusive ref
// v v
let _b = a.clone().iter_mut();
// ^^^^^^^^^
// A temporary `Vec<i32>`
The temporary returned from a.clone()
hasn't been assigned to a non-mut
binding, so it doesn't get the lint-like restrictions about not taking a &mut _
. So there's no "complaining" about not declaring mut
here. There isn't a way to get the lint-like restrictions on temporaries.
There's still a question outstanding: who owns the temporary? We'd have to define "ownership" in some way to answer this well, I suppose. It's easier to answer the question if rephrased: where does the temporary drop? In this case, it drops at the end of the statement. This means that _b
is actually not usable -- try to use it and you'll get an error about the temporary dropping too soon.
Sometimes temporaries drop in the surrounding scope instead. Here's a good article on the topic.
I guess you could say that whatever scope the temporary is going to drop in owns the temporary. If you want to be able to change that, you have to bind the value returned from a.clone()
into a variable instead (which you could then pass somewhere else).
No, the borrow checker still applies. It just doesn't have a problem with this case.
No, forgetting to use let mut
instead of let
or having to mechanically change let mut
to let
to silence warnings are trivialities as far as satisfying borrow check go. Don't let that drive your decisions.