I am in favor of downgrading this error to a deny-by-default lint.
mut in bindings strikes me as a misfeature because it can't decide what it is:
-
It deviates from the more common and much more useful meaning of
mutin Rust, which is "exclusive". As others have already mentioned, learning thatmutdoesn't mean "mutable" but "exclusive" is already an important step for people learning Rust. Except, in bindings, it does mean "mutable", since "exclusive" would be redundant.C and keyword reuse
This situation is reminiscent of how, in C,
staticmeans non-auto, except whenautoisn't sensible, in which case it means non-extern. In Rust,mutmeans exclusive, except when sharedness isn't sensible, in which case it means mutable. In C,autoandexterncan't be combined, so they use the same keyword to negate whichever one is applicable. This confuses people learning the language, sincestaticdoesn't mean the same thing in all common contexts. I see the same confusion withmutin Rust.It may be notable that unlike
staticin C, the two meanings ofmutin Rust can be combined, in some cases leading to patterns like&mut mut foo.It's natural to start by explaining
mutas meaning mutable, and refine the concept later. But this refinement is incomplete, because we still have some parts of the language wheremutactually does just mean mutable. This dichotomy makes it tempting to say things like Rust'smutkeyword actually has two meanings. In a pattern it means "mutable" and in a reference type it means "exclusive". But is that really true? -
Variables declared without
mutcan't be borrowed exclusively, which breaks even the already complicated mental model above. So there is a sense in which the twomuts are connected, since you can't take a&mutreference to a non-mutvariable. But under the "&mutmeans exclusive and patternmutmeans mutable" model, there's no good reason for this. So the conclusion is that patternmutmeans both mutable and exclusive.(But even this is subtly misleading, because
&mutreferences can be reborrowed without being markedmut.)
Consider the learner's journey from "mut means mutable" to "mut means exclusive" to "mut sometimes means exclusive and sometimes mutable" and here we are at "mut usually means exclusive but in a pattern it means mutable and exclusive, except &mut T is always exclusive." Can we not at least find a way to simplify that final step, if not eliminate it entirely?
If pattern mut were only a lint against rebinding the name (let foo = 1; foo = 2) it would be reasonable to say that Rust really does have two muts. Would there be interest in downgrading E0596 (cannot borrow `x` as mutable, as it is not declared as mutable) to a deny-by-default lint, while leaving E0384 ( cannot assign twice to immutable variable `x`) unchanged?