Rust: Use 'mut' as alias for 'let mut'

Created on 9 Oct 2019  路  20Comments  路  Source: rust-lang/rust

The idea of shortening let mut to var has been discussed many times before, and the general consensus always ends up being that it is not a good idea. I would like to propose something a bit different, so please hear me out.

I propose that instead of aliasing var to let mut, we instead just make mut expand into let mut when in the context of a variable declaration. For example:

let mut foo = 12;
// is identical to
mut foo = 12;

This solves the problems (readability, more concise) that people have with let mut, whilst not causing the issues that people have (pattern matching and consistancy).

The most common issue people have with aliasing var to let mut is that it doesn't fit well with patterns, like let (x, mut y) = .... However, mut gets rid of this problem, because you wouldn't write let mut (x, mut y) if you were trying to pattern match, so why would someone want to write mut (x, mut y)? You wouldn't do that, and it wouldn't compile.

This would be optional, of course. let mut x = 1 would still work fine, and people would just have to option of removing the let to let it be implied.

There are a lot of other abbreviations used in rust (fn, mod, pub) that all serve the purpose of writing less. It just doesn't feel right that one of the most commonly used keywords is so long.

:heart::crab:


A-diagnostics A-suggestion-diagnostics C-enhancement D-newcomer-roadblock E-easy E-help-wanted E-mentor P-medium T-compiler

Most helpful comment

It seems unlikely to me that we would support mut $ident = $expr; as a short-hand. I'm personally opposed to it both because it adds unnecessary compiler complexity, stylistic schisms, and for the reasons outlined by @KrishnaSannasi.

However, I've taken the liberty of reusing this issue towards improved diagnostics:

1.

mut x = 0;
--- help: missing `let`

The idea here is to check self.token.is_keyword(kw::Mut) && self.look_ahead(1, |t| t.is_ident()).

2.

var x = 0;
--- help: to introduce a variable, write `let` instead of `var`

auto x = 0;
---- help: to introduce a variable, write `let` instead of `auto`

The idea here is to check:

is_ident_var_or_auto && !self.look_ahead(1, |t| is_brace(t))`

The relevant code is here: https://github.com/rust-lang/rust/blob/ece4977138a8eda96c234982e482fb43f67f1bee/src/libsyntax/parse/parser/stmt.rs#L44-L49

cc @estebank

All 20 comments

Rust wants to make to make you think when you try and use mutation, even if only a little bit, because mutation can be difficult to reason about. Shortening let mut to mut doesn't fit this philosophy. Also, this makes mutation just as easy as no mutation. This will lead people to choose mutation because it will give them less compiler errors in the short term, and in doing so open themselves up to some weird bugs that could have been prevented with the syntactic salt that is let mut.

tldr: var wasn't rejected because of the name var, but because it allowed easy mutation, which runs counter to Rust's philosophy.

This will lead people to choose mutation because it will give them less compiler errors in the short term, and in doing so open themselves up to some weird bugs that could have been prevented with the syntactic salt that is let mut.

I politely disagree. In fact, it is actually the other way around. Using mut unnecessarily will cause compiler warnings, discouraging the use of unneeded mutability. The compiler doesn't warn when the use of mut is actually needed.

I politely disagree. In fact, it is actually the other way around. Using mut unnecessarily will cause compiler warnings

The fact that people will choose mut in the face of compiler warnings has been shown in practice, across many projects. Also, the problem isn't that people may choose to put mut just for the heck of it, but that they will unintentionally mutate something that was supposed to be immutable. And because it is being mutated, there is no warning, and they used mut because it is their default, it is their default because it gets rid of compiler errors that say they were trying to mutate things that are immutable even though they weren't supposed to mutate them in the first place. They will use mut because it is just as easy as making it immutable. So the problem is a bit more subtle than you seem to think.

This solves the problems (readability, more concise) that people have with let mut

Incorrect. Extreme brevity harms readability. See Perl. lol

Readers would ignore the let vs mut distinction far more often that let vs let mut, partially due to length difference, but let mut works even better than say mutable.

It's true boilerplate often harms readability, but boilerplate requires some complexity be repeated, not merely the let in let mut.

It harms readability even further if mut also works for let mut, especially when you consider code readers need not actually know Rust.

Rust syntax was designed largely to reduce cognitive load, not to reduce typing.

We use return, continue, and default, not any shorter alternatives. And delegation RFCs have kinda given up the short syntaxes. I think Rust's omission of then is now considered a mistake, but presumably helps C programmers.

Rust has short keywords only really for fundamental "items" used repeatedly by basically every module. It's similar cognitive load if you write fn, func, or function, but actually just writing the shorter bare f(..) { .. } increases cognitive load. I think var invokes the minor linguistic subtlety that variable means mutable binding, while let acts like a common term for binding, which makes let mut very slightly clearer.

whilst not causing the issues that people have (pattern matching and consistancy)

The mut statement would be orthogonal to the current language in some sense though. let mut foo is not a special syntax, but is composed of let statement + mut foo pattern. It is in line with other places expects a pattern, where you can write match _ { mut x => ... }, for mut x in, or fn foobar(mut x: i32).

It seems unlikely to me that we would support mut $ident = $expr; as a short-hand. I'm personally opposed to it both because it adds unnecessary compiler complexity, stylistic schisms, and for the reasons outlined by @KrishnaSannasi.

However, I've taken the liberty of reusing this issue towards improved diagnostics:

1.

mut x = 0;
--- help: missing `let`

The idea here is to check self.token.is_keyword(kw::Mut) && self.look_ahead(1, |t| t.is_ident()).

2.

var x = 0;
--- help: to introduce a variable, write `let` instead of `var`

auto x = 0;
---- help: to introduce a variable, write `let` instead of `auto`

The idea here is to check:

is_ident_var_or_auto && !self.look_ahead(1, |t| is_brace(t))`

The relevant code is here: https://github.com/rust-lang/rust/blob/ece4977138a8eda96c234982e482fb43f67f1bee/src/libsyntax/parse/parser/stmt.rs#L44-L49

cc @estebank

@rustbot claim

Error: Parsing assign command in comment failed: ...tbot claim|error: expected end of command at >| :)...

Please let @rust-lang/release know if you're having trouble with this bot.

@Centril Although I would also guess that the initial idea is very unlikely to end up accepted, it feels premature to unilaterally hijack the thread in your very first comment and make it have a different (and in a way opposite) purpose. Teams of the Rust project are designed to make decisions collectively. Improving diagnostics sounds good, but issue numbers are cheap.

@Centril Although I would also guess that the initial idea is very unlikely to end up accepted, it feels premature to unilaterally hijack the thread in your very first comment and make it have a different (and in a way opposite) purpose.

Implementing something as a diagnostic actually brings you closer to a working implementation -- I think more proposals should start out as diagnostics.

Teams of the Rust project are designed to make decisions collectively. [...]

I will not bother the other language team members with not-fully-formed proposals that we will not accept in either case. I converted the issue into the most actionable and productive thing it could be.
The place for ephemeral discussions is either on Discord or on internals.

If you want to discuss this further, I'm available at Discord (but will not respond to it further here).

[...], but issue numbers are cheap.

...but wasting everyone's time with things that will go nowhere (because it won't be accepted and because it is not important enough to be raised at the team) is not.

IMHO, the original thing this issue was opened for should be an RFC, not an issue. I'd have closed this, pointed the OP to the RFCs repo, and opened a new issue for the diagnostic issue.

This issue was literally in the RFCs repo initially, @Centril transferred it from there.

Incorrect. Extreme brevity harms readability. See Perl. lol

@burdges Language bashing is not welcome here

@steveklabnik Wait, so should I unassign here, and assign to the new issue?

@chocol4te Nah, just use this one.

Yeah, thats fine. You can just change this issue to be for diagnostics instead.

I'm sorry, I've been pretty busy with coursework, and I'm still fighting my Python installation to even build the compiler, let alone make progress on this. Sorry :(

I'll assign this to myself to work on when I have time. If you want to work on it however, feel free to work-steal it from me!

I would like to help with this

@JesterOrNot Sorry; should have updated this issue...! There's a PR https://github.com/rust-lang/rust/pull/65811 at the moment. Feel free to leave suggestions on it if you like. :)

Was this page helpful?
0 / 5 - 0 ratings