Skip to content

Commit 5c623a1

Browse files
committed
rfc, semicolon-items: initial version.
1 parent 90a6f4e commit 5c623a1

File tree

1 file changed

+220
-0
lines changed

1 file changed

+220
-0
lines changed

text/0000-semicolon-items.md

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
# Semicolon Items
2+
3+
- Feature Name: `semicolon_items`
4+
- Start Date: 2018-06-17
5+
- RFC PR:
6+
- Rust Issue:
7+
8+
# Summary
9+
[summary]: #summary
10+
11+
The semicolon (`;`) is now accepted as an item, permitting a user to write
12+
`struct Foo {};`, among other things. The `item` fragment specifier in a
13+
`macro_rules` matcher will match `;`.
14+
15+
# Motivation
16+
[motivation]: #motivation
17+
18+
## Leftover semicolons when refactoring
19+
20+
Various common refactorings often leave behind extraneous semicolons, such as:
21+
22+
* Replacing a closure with a `fn` item:
23+
24+
```rust
25+
let add_two = |x| x + 2;
26+
27+
// becomes:
28+
fn add_two(x: u32) -> u32 {
29+
x + 2
30+
};
31+
```
32+
33+
* Adding a field to what was formerly a unit struct:
34+
35+
```rust
36+
struct UnitExpr;
37+
38+
// becomes:
39+
struct UnitExpr {
40+
span: Span,
41+
};
42+
```
43+
44+
* Changing a tuple struct into a braced struct:
45+
46+
```rust
47+
struct Foo(String, usize);
48+
49+
// becomes:
50+
struct Foo {
51+
bar: String,
52+
baz: usize,
53+
};
54+
```
55+
56+
The error emitted by the compiler in these circumstances is never
57+
indicative of a bug, but nonetheless disturbs the flow of writing
58+
and causes the user to have to wait and recompile.
59+
During that time, the user's train of thought can easily be lost.
60+
61+
## Improving consistency with items in `fn` bodies
62+
63+
[compiletest_rs]: https://github.com/laumann/compiletest-rs/blob/master/src/runtest.rs#L2585-L2660
64+
65+
Incidentally, these extraneous semicolons are currently permitted on items
66+
defined inside of `fn` bodies, as a consequence of the fact that `;` in
67+
isolation is a valid *statement* (though not an item).
68+
The following, slightly modified example is from [compiletest_rs].
69+
70+
```rust
71+
fn read2_abbreviated(mut child: Child) -> io::Result<Output> {
72+
use std::mem::replace;
73+
use read2::read2;
74+
75+
const HEAD_LEN: usize = 160 * 1024;
76+
const TAIL_LEN: usize = 256 * 1024;
77+
78+
enum ProcOutput {
79+
Full(Vec<u8>),
80+
Abbreviated {
81+
head: Vec<u8>,
82+
skipped: usize,
83+
tail: Box<[u8]>,
84+
}
85+
};
86+
87+
impl ProcOutput {
88+
...
89+
};
90+
91+
...
92+
}
93+
```
94+
95+
By permitting semicolons as items outside of `fn` bodies,
96+
a modicum of consistency in the Rust's grammar can be gained.
97+
98+
## A frequent editing mistake
99+
100+
For the authors of this RFC, who have written Rust code for many years,
101+
it still sometimes happens that they mistakenly place a semicolon after
102+
`struct Name {}`. As previously mentioned, such trivialities needlessly
103+
disrupt the editing process.
104+
105+
## Retaining a uniform style
106+
107+
To retain as uniform of a style possible in the Rust community,
108+
this RFC proposes that `rustfmt`, Rust's code formatting tool,
109+
should remove extraneous `;` since they are most likely left over
110+
while editing or as a frequently made mistake.
111+
No possibility of configuring this behavior of `rustfmt` is proposed at this time.
112+
113+
# Guide-level explanation
114+
[guide-level-explanation]: #guide-level-explanation
115+
116+
Simply put, the token `;` is now accepted as an item.
117+
This means that you are allowed to write all of the following:
118+
119+
```rust
120+
struct Foo {
121+
bar: Baz
122+
}; // <-- NOTE
123+
```
124+
125+
You can also put a `;` after `enum`, `trait`, `impl`, `fn`, `union`,
126+
and `extern` items.
127+
128+
A `macro_rules` matcher using the `item` fragment will now also accept
129+
`;` as an item. For example, given:
130+
131+
```rust
132+
macro_rules foo! {
133+
($x: item) => { .. }
134+
}
135+
```
136+
137+
you may write:
138+
139+
```rust
140+
foo!(;)
141+
```
142+
143+
It's important to note that while `;` is now accepted where `item`s are,
144+
this is not intended as the recommended style, but only to improve the
145+
consistency in Rust's grammar, as well as writing flow.
146+
147+
# Reference-level explanation
148+
[reference-level-explanation]: #reference-level-explanation
149+
150+
1. The token `;` is accepted as a valid item, both in the language syntax
151+
and by the `item` macro 'fragment specifier'. As an example, `struct Foo {};`
152+
is therefore in the language accepted by a Rust compiler.
153+
154+
2. `rustfmt` will remove any extraneous `;` items.
155+
156+
# Drawbacks
157+
[drawbacks]: #drawbacks
158+
159+
The language accepted by a Rust parser is made somewhat more complicated;
160+
this can have some minor effect on parsing performance and will also
161+
complicate the language users will need to understand.
162+
However, we believe that nothing new is actually necessary to be learned
163+
as a redundant `;` in an example such as `struct Foo {};` should already
164+
be intuitive for someone who knows `struct Foo {}`.
165+
166+
# Rationale and alternatives
167+
[alternatives]: #rationale-and-alternatives
168+
169+
## Do nothing
170+
171+
As always, if we conclude that the motivation is not enough,
172+
we can elect to do nothing.
173+
174+
## Improve error messages further
175+
176+
An idea due to [@joshtripplet](https://github.com/joshtriplett) is to improve
177+
the error messages given by `rustc` instead of accepting redundant `;`s.
178+
179+
The current error message when writing `struct Foo {};` is:
180+
181+
```rust
182+
error: expected item, found `;`
183+
--> src/main.rs:1:14
184+
|
185+
1 | struct Foo {};
186+
| ^ help: consider removing this semicolon
187+
```
188+
189+
This error message is already quite good, giving the user actionable
190+
information. However, the specific case could be improved by recognizing it
191+
specially and saying something like *"don't put a `;` after a `struct { ... }`"*
192+
(same for `union`s and `enum`s). This idea is being discussed in [issue #51603](https://github.com/rust-lang/rust/issues/51603).
193+
194+
However, this does not solve the problem when refactoring,
195+
and neither does it enhance writing flow nor make the grammar more consistent.
196+
197+
# Prior art
198+
[prior-art]: #prior-art
199+
200+
Rust is unusual in that it is a semicolon-terminated language,
201+
yet it has very few kinds of statements that are not expressions in disguise.
202+
This makes direct comparisons to other languages difficult.
203+
204+
There is however some prior art:
205+
206+
* C++11 [added support](http://en.cppreference.com/w/cpp/language/declarations)
207+
for `;` as an "empty declaration," where prior specifications of the language did not.
208+
209+
* GHC, the Glasgow Haskell Compiler, accepts `;` at the top level.
210+
For example, the following is accepted by GHC:
211+
212+
```haskell
213+
module Foo where
214+
;
215+
```
216+
217+
# Unresolved questions
218+
[unresolved]: #unresolved-questions
219+
220+
* Does this create parsing ambiguities anywhere?

0 commit comments

Comments
 (0)