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
mut
in Rust, which is "exclusive". As others have already mentioned, learning thatmut
doesn'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,
static
means non-auto
, except whenauto
isn't sensible, in which case it means non-extern
. In Rust,mut
means exclusive, except when sharedness isn't sensible, in which case it means mutable. In C,auto
andextern
can't be combined, so they use the same keyword to negate whichever one is applicable. This confuses people learning the language, sincestatic
doesn't mean the same thing in all common contexts. I see the same confusion withmut
in Rust.It may be notable that unlike
static
in C, the two meanings ofmut
in Rust can be combined, in some cases leading to patterns like&mut mut foo
.It's natural to start by explaining
mut
as meaning mutable, and refine the concept later. But this refinement is incomplete, because we still have some parts of the language wheremut
actually does just mean mutable. This dichotomy makes it tempting to say things like Rust'smut
keyword 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
mut
can't be borrowed exclusively, which breaks even the already complicated mental model above. So there is a sense in which the twomut
s are connected, since you can't take a&mut
reference to a non-mut
variable. But under the "&mut
means exclusive and patternmut
means mutable" model, there's no good reason for this. So the conclusion is that patternmut
means both mutable and exclusive.(But even this is subtly misleading, because
&mut
references 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 mut
s. 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?