Introduction and background
When I ran rustfmt
yesterday on some code I was writing, I was once again reminded by the fact that our current defaults are the following:
where_trailing_comma <boolean> Default: false
Put a trailing comma on where clauses
struct_trailing_comma [Always|Never|Vertical] Default: Vertical
If there is a trailing comma on structs
struct_lit_trailing_comma [Always|Never|Vertical] Default: Vertical
If there is a trailing comma on literal structs
enum_trailing_comma <boolean> Default: true
Put a trailing comma on enum declarations
match_block_trailing_comma <boolean> Default: false
Put a trailing comma after a block based match arm (non-block arms are not affected)
match_wildcard_trailing_comma <boolean> Default: true
Put a trailing comma after a wildcard arm
I know that some of this is ongoing discussion in https://github.com/rust-lang-nursery/fmt-rfcs/issues/34, since it specifically mentions the trailing comma in some situations:
The last match arm should have a comma if the body is not a block:
I want to take the chance before #34 is merged to broaden that discussion a bit more, and try to reach consensus on trailing commas in general. (I know that they are sometimes needed; I consider that a bug and this discussion is more related to the areas where it is syntactically optional, but a common de-facto standard in the Rust community and/or enforced by rustfmt
.)
I don't claim to be unbiased; I have a specific opinion that I am voicing in this RFC. However, what I do claim is to represent the different standpoints in a factual way. If you feel that I am missing or misrepresenting some argument for either position, please write a comment with the missing part and I'll try to adjust the proposal continously.
Basic presumption
I hope that (almost) everyone can agree that our current position - to sometimes advocate a trailing comma and sometimes not, as shown above - is not optimal. It feels counterintuitive and also like something you'd have to teach people about ("the style guide recommends it here, but not there, for whatever reason" - not saying that there aren't reasons for the inconsistency, just saying that an inconsistent set of rules in an area is much harder to defend and also harder for people to remember.)
Arguments used to support the addition of trailing commas
It's commonly used within the rust community
This is a fact. Servo uses it, rustc
uses it, probably a bunch of other projects as well. The precedent is there, there's not so much to say about that. There are probably a number of people who like this style, otherwise it wouldn't have been started to be used anyway. Moving away from this style would risk discouraging those people from contributing to the community.
Counter-argument
- It's not that common outside the Rust community.
- The current Rust code in existance will hopefully eventually be a very small minority of the cumulative amount of rust code in the years to come. In other words, the vast majority of the rust code eventually written does not yet exist.
- Once
rustfmt
is stable enough, we should be able to reformat larger codebases (like servo and rustc) with it anyway.
- Regarding the "people might like it" part, it's a fact regarding any part of the style guide. The style guide cannot emphasise individual people so much, but should rather focus on the greater good for the community as a whole.
It makes changing the code easier
This argument is also factually correct; when adding a new entry to an array, you never have to remember adding a trailing comma. Perhaps more importantly so, when rearranging items in an array you don't have to visually scan the array to find which line now has the missing comma (so you can add it and make the code compile again).
Counter-argument
While the argument is factually correct, it's pretty much a matter of what you are used to. Good tooling (i.e. instant feedback on the validity of the code, perhaps powered by rls or simiilar) can make this be more of a moot point. If the editor you are using instantly shows you (or hints at) where the missing comma is, this is less of a point as before. (but of course, not all people can be expected to use a single editor/a RLS-powered editor etc.)
It produces shorter, less verbose diffs
This argument is based on the simple fact that if the trailing comma is already present on the last line of an {array|enum|match|etc}, that line will not have to be edited if you add a new element to the {array|enum|match}. In other words, the diff produced when changing the code will only include the relevant line (the new functionality being added), you won't have to touch an adjacent line to make it compile.
This is a very relevant and factual argument, since it avoids the cognitive load put on the brain when parsing the diff. You can focus on the actual change, and have to think less about the noise.
Counter-argument
While this is a a factually correct assertion, @bbatsov (the author of Rubocop, a linter for Ruby), wrote that:
The defaults will not be changed [to enable trailing commas by default] - using VCS diff limitations to justify a coding convention doesn't make sense
Of course, here is where facts end and opinions start: I personally agree with Boris, that this is too weak an argument to justify the current position. Others might, and will, say the opposite (that this is such a strong argument that the current position should be retained).
It breaks git blame
This is actually a pretty good argument. It will appear as if the person who added the comma was the author of that line even though his/her contribution was only the single character. Can't really say much about this, this is a reasonable, factual argument. (However, it's again letting the limitations of a particular tool mandate the style choices)
Arguments used to reject the addition of trailing commas
Opinionated: It looks like a typo
One of my early encounters with the Rust community was actually because of this; I read some code in the Rust compiler source code which struck me as "looking weird", because of the trailing comma. I have been doing programming for about 20 years, of which 14 years of it has been "for a living". Never during these years have I encountered a community that was advocating trailing commas.
I'm quite certain I'm not alone with this experience (which the SO links further down in this RFC indicates; people tend to get confused when they read code written like this). In my eyes, the current position definitely is not "mainstream" enough for a language with high ambitions to become a broad competitor to languages like C, C++ and perhaps Java, C# and others as well. If that's really what we're aiming for, we should try to aim for a style that "feels natural" to most people reading code without having seen what the style guide advocates.
Our language is so great because of the other features it provides (immmutability, the whole borrowing concept, proper concurrency etc) that I think that is what should be sticking out when people read our code: our superiority in these areas. Not that we write code that looks like an array element is missing from the end; that someone has forgotten to finish his sentence. Again, extremely opinionated, but it makes people question the code: is this really what the author intended to write? Is there a bug here, did I just discover an unpleasant surprise?
It is contrary to the recommended style guide/linter defaults for other programming languages
A non-comprehensive on some of the findings I could gather:
- rubocop, a linting tool for Ruby, produces warnings for this syntax with its default config. The issue is discussed more in https://github.com/bbatsov/rubocop/issues/713, https://github.com/bbatsov/rubocop/pull/735 and at length in https://github.com/bbatsov/ruby-style-guide/issues/273. Other style guides like the Thoughtbot one advocates trailing comma, but I'd say the former one is the more popular one (and definitely a more comprehensive guide with much more details about the recommendations).
- eslint, a linting tool for Javascript, has it disabled by default but with options for enforcing it for various scenarios (multi-line only, always, etc).
- tslint, a linter for Typescript, seems to take the approach to let you configure it either way, but don't seem to advocate a particular default. (tslint is not a reformatter in that sense, so it can take a more relaxed stance on the matter)
- The PHP manual says that "[h]aving a trailing comma after the last defined array entry, while unusual, is a valid syntax."
- It has been said to be common in Python, but I couldn't find a style guide to back this claim.
Other languages/style guides don't typically disallow it, but it's a common source for people to scratch their heads and wonder "why is this valid C#/Python/etc code?". Take a look at this and this link (Python), this link (C#), this link (Java) etc. It is definitely not a universally accepted style, recommended by a large majority of the style guides for programming languages in general or anything like that.
To put it bluntly: if we want to keep making people confused, we should keep the current recommendation as it is.
It is invalid code in some languages/environments:
-
JSON: Valid code
{
"foo": "bar",
"baz": "zot"
}
Invalid code, parsers will reject this:
{
"foo": "bar",
"baz": "zot",
}
Opinionated: It's not really intended for humans writing code
The fact that virtually all programming languages (except from JSON, which isn't really a "programming language") allows it seems to be generally motivated by the fact that it's intended for code generation, where it's obviously a whole lot easier for the code generating code to not have to always check "is this the last element of the array being printed to stdout" and so forth. C# 2005 Programmer's Reference (p. 208) seems to imply this, and this SO thread gives the same impression. It was initially allowed in C back from the days of K&R, and many other languages have included support for it since.
One could argue that if this "feature" is designed for code generators, let the code generators use it and let humans produce code that is more aesthetically pleasing and straightforward.
Conclusion and proposed adjustment
I find the communities for various programming languages a bit scattered on the subject. Some people prefer this style very strongly, others (like me) dislike it equally strongly and would never ever want to run a tool to reformat our code with the default settings as they are right now. Others are just confused by reading code formatted like this and start to ask questions about it. That, to me, indicates that our current defaults are not so intuitive and hence I suggest that the following settings should be the new default:
where_trailing_comma <boolean> Default: false
Put a trailing comma on where clauses
struct_trailing_comma [Always|Never|Vertical] Default: Never
If there is a trailing comma on structs
struct_lit_trailing_comma [Always|Never|Vertical] Default: Never
If there is a trailing comma on literal structs
enum_trailing_comma <boolean> Default: false
Put a trailing comma on enum declarations
match_block_trailing_comma <boolean> Default: false
Put a trailing comma after a block based match arm (non-block arms are not affected)
match_wildcard_trailing_comma <boolean> Default: false
Put a trailing comma after a wildcard arm
IMHO, this would be very consistent and easy to understand for anyone: experts and new Rust enthusiasts alike.
Now, you may send the expected flames... 😉
has-PR