The Pre-RFC process
Here I'd mostly like the following discussed:
- Are there more motivations?
- Do the drawbacks sufficiently cover the situation? are there more?
- Are any cases missing?
In other words: I'd like help with describing the situation as fairly, honestly, and accurately as possible.
The RFC
- Feature Name:
leading_comma
- Start Date: 2018-04-<TODO>
- RFC PR: (leave this empty)
- Rust Issue: (leave this empty)
Summary
Permit leading commas everywhere trailing commas are permitted.
An example:
let array = [
, 1
, 2
, 3
];
Motivation
This RFC is purely about style and accomodating the tastes and formatting habbis of more people.
What this RFC is not for
This RFC does not suggest that everyone should adopt leading commas as their
new one-true-style. In particular, this RFC does absolutely not change
the current rustfmt
style.
This RFC does also not proscribe how one should style leading commas either when used and leaves up to people or a style RFC.
It is in line with Rust's philosophy to have a tolerant grammar
In particular, RFC 1925 was accepted to allow a leading |
in match
and
then it was said that:
We tend to support a flexible grammar when possible, including things we don't consider idiomatic at all. Philosophically, this is an RFC we are inclined to accept, and there'd probably have to be a pretty strong, practical reason for us not to accept it.
We accept leading semicolons
In other words, the following is a legal Rust program:
fn foo() {}
fn main() {
; foo()
; foo()
}
This style of formatting is probably quite uncommon though.
It should however be noted that we accept an arbitrary number of semicolons anywhere in a block, so the following program is also valid:
fn main() { ; ; ; }
Leading commas read like bullet points
To answer the question "Why would anyone want leading commas?"
Consider the following type:
struct Foo {
, alpha: A
, beta: B
, gamma: G
}
and compare that to:
- alpha: Alpha conversion is the process of renaming variables.
- beta: Beta reduction is a fancy term for reducing function application to a value.
- gamma: An uppercase gamma (
Đ“
) is often the typing environment.
A similarity here is that ,
acts like the spine of a list as in the bullet
point list above.
The commas can also be, for some people, more visually clear compared to commas at the end. To some, this is however a drawback as they would see the list spine as noise.
Guide-level explanation
In places where trailing commas are permitted, leading commas are now also permitted.
Let's go through a few examples of such places. Note that some of these uses can look quite strange but are included for consistency and generality. We also illustrate some different possible styles even tho they may not be optimal for leading commas.
Arrays and vec![..]
let arr = [
, 1
, 2
, 3
];
let vec = vec![
, "Alan Turing"
, "Joan Clarke"
, "Hugh Alexander"
];
Structs
As seen above in the motivation, we can use leading commas in struct
s:
struct Thing {
, velocity: (f64, f64)
, color: Color
, autonomous: bool
}
and the associated pattern:
let Thing {
, velocity
, color
, autonomous
} = foo;
Unions
union Foo {
, as_i32: i32
, as_u32: u32
}
Enums
enum Foo<T> {
, Recv(
, usize
, T
)
, Send {
, id: usize
, msg: T
}
}
Match
and the associated patterns:
match foo {
, Foo::Recv(
, a
, b
) => recv(a, b)
, Foo::Send
{
, id
, msg
} => send(id, msg)
}
Tuples
let (, b): (, usize) = (, 2);
// equivalent to:
let (b,): (usize,) = (2,);
let (, a, b, c): (, usize, usize, usize) =
(
, 42
, 1337
, 42
);
// equivalent to:
let (a, b, c): (usize, usize, usize) = (1, 2, 3);
Function arguments, universal quantification sites, and where
clauses
fn foo<
,'a
, Bar
, Quux
>(
, bar: Bar,
, quux: Quux
) -> Foo<
, Bar
, Quux
> where
, T: Clone
, U: Copy
{
}
Turbofish
let x = alpha::<, Foo, Bar>();
#[derive]
and #[attribute]
in general
#[derive(
, Copy
, Clone
)]
struct Foo(
, u8
, u8
);
Reference-level explanation
Anywhere in the grammar where a list of commas are accepted, a leading comma is also accepted.
Note in particular that this means that the following is accepted:
struct Foo { , a: T, b: T, } // Both leading and trailing comma.
Since the change is technically trivial, a full grammar diff is not given.
This also applies to macros in the standard library.
Drawbacks
Some people will not want to read this in the code of others
Simply put, the style introduced will not fit the taste buds of a majority of rustaceans, and they will not wish to see this style in code of other people. Many people will find the syntax noisy.
To mitigate this concern, the fmt
style guide will keep recommending
the current formatting.
Learnability
It will take more time to learn the language as more grammatically valid forms can now be found in the wild. However, not all grammatical forms are know by expert Rustaceans and they get by anyways. We can mitigate this by not teaching the form anywhere else than in the reference or in a very late section so that it does not have a cost for beginners.
It complicates the grammar
It compilcates the grammar and therefore probably also syn
.
These complications can make rustc
's parser minutely less performant since it now has to look for an optional comma where a comma separated list is accepted.
As part of the complication in supporting leading commas, source code parsers for other projects will have to be updated and so will syntax highlighters to support the new and modified grammar of Rust. However, this is nothing new and happens every time the grammar of Rust changes. Many highlighters such as the internals forum and in VSCode already handle this fine.
The benefits to macros are very tenuous at best
Given that trailing commas are already supported, there does not seem to be an improvement to macros from allowing leading commas. Leading commas may lead to an expectation that comma-separated list-taking macros should support leading commas, which can make it harder to write macros. However, this is not a must, and so libraries can opt to not support leading commas.
Rationale and alternatives
The only alternative is to not introduce leading commas.
The impact of that would be to not accomodate the punctuation style desires of a minority of people.
Clean diffs
Trailing commas lead to clean diffs, but so do leading commas as well.
Easier multi-cursor support
In many editors such as Sublime Text it is relatively easy to add a comma to
the start of every line. In the editior mentioned, you only need to press
<cmd>
+ shift
+ L
+ home
(modulo the <cmd>
key) to do that.
In VSCode (with the shunt
extension), you can do the same with
ctrl
+ shift
+ O
.
It is often equally easy to multi-select select the end of every line in a selection, so this is an argument both in favor of leading and trailing commas (the latter of which we already support).
Prior art
Haskell
In Haskell, it is quite common to style things, in particular lists, with a leading comma, but Haskell does not actually accept leading commas:
exceptions =
[ InvalidStatusCode
, MissingContentHeader
, InternalServerError
]
however, @joshtriplett said:
I’ve heard multiple experienced Haskell programmers say “this is a thing we do because we can’t use trailing commas”.
For me, that is certainly how my use of leading comma began. But to be entirely honest, if Haskell were to one day suddenly begin supporting both trailing and leading commas, I would start writing what @centril wrote. Leading punctuation makes structure more obvious, because it lines up.
Other languages
In general, it seems uncommon for languages to support leading commas.
Unresolved questions
- Have any cases been missed / forgotten in the RFC?