Lack of `mut` in bindings as a deny-by-default lint

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:

  1. It deviates from the more common and much more useful meaning of mut in Rust, which is "exclusive". As others have already mentioned, learning that mut 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 when auto 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 and extern can't be combined, so they use the same keyword to negate whichever one is applicable. This confuses people learning the language, since static doesn't mean the same thing in all common contexts. I see the same confusion with mut in Rust.

    It may be notable that unlike static in C, the two meanings of mut 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 where mut actually does just mean mutable. This dichotomy makes it tempting to say things like Rust's mut keyword actually has two meanings. In a pattern it means "mutable" and in a reference type it means "exclusive". But is that really true?

  2. 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 two muts are connected, since you can't take a &mut reference to a non-mut variable. But under the "&mut means exclusive and pattern mut means mutable" model, there's no good reason for this. So the conclusion is that pattern mut means both mutable and exclusive.

    (But even this is subtly misleading, because &mut references can be reborrowed without being marked mut.)

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?

6 Likes