Skip to content

Commit 555bb86

Browse files
committed
RFC: Change DST syntax to T: Sized?
1 parent 138a079 commit 555bb86

File tree

1 file changed

+178
-0
lines changed

1 file changed

+178
-0
lines changed

text/0000-dst-syntax.md

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
- Start Date: 2014-11-29
2+
- RFC PR: (leave this empty)
3+
- Rust Issue: (leave this empty)
4+
5+
Summary
6+
=======
7+
8+
Change the syntax for dynamically sized type parameters from `Sized? T` to `T:
9+
Sized?`, and change the syntax for traits for dynamically sized types to `trait
10+
Foo: Sized?`. Extend this new syntax to work with `where` clauses.
11+
12+
Motivation
13+
==========
14+
15+
History of the DST syntax
16+
-------------------------
17+
18+
When dynamically sized types were first designed, and even when they were first
19+
being implemented, the syntax for dynamically sized type parameters had not been
20+
fully settled on. Initially, dynamically sized type parameters were denoted by a
21+
leading `unsized` keyword:
22+
23+
```rust
24+
fn foo<unsized T>(x: &T) { ... }
25+
struct Foo<unsized T> { field: T }
26+
// etc.
27+
```
28+
29+
This is the syntax used in Niko Matsakis’s [initial design for
30+
DST](http://smallcultfollowing.com/babysteps/blog/2014/01/05/dst-take-5/). This
31+
syntax makes sense to those who are familiar with DST, but has some issues which
32+
could be perceived as problems for those learning to work with dynamically sized
33+
types:
34+
35+
- It implies that the parameter *must* be unsized, where really it’s only
36+
optional;
37+
- It does not visually relate to the `Sized` trait, which is fundamentally
38+
related to declaring a type as unsized (removing the default `Sized` bound).
39+
40+
Later, Felix S. Klock II [came up with an alternative
41+
syntax](http://blog.pnkfx.org/blog/2014/03/13/an-insight-regarding-dst-grammar-for-rust/)
42+
using the `type` keyword:
43+
44+
```rust
45+
fn foo<type T>(x: &T) { ... }
46+
struct Foo<type T> { field: T }
47+
// etc.
48+
```
49+
50+
The inspiration behind this is that the union of all sized types and all unsized
51+
types is simply all types. Thus, it makes sense for the most general type
52+
parameter to be written as `type T`.
53+
54+
This syntax resolves the first problem listed above (i.e., it no longer implies
55+
that the type *must* be unsized), but does not resolve the second. Additionally,
56+
it is possible that some people could be confused by the use of the `type`
57+
keyword, as it contains little meaning—one would assume a bare `T` as a *type*
58+
parameter to be a type already, so what does adding a `type` keyword mean?
59+
60+
Perhaps because of these concerns, the syntax for dynamically sized type
61+
parameters has since been changed one more time, this time to use the `Sized`
62+
trait’s name followed by a question mark:
63+
64+
```rust
65+
fn foo<Sized? T>(x: &T) { ... }
66+
struct Foo<Sized? T> { field: T }
67+
// etc.
68+
```
69+
70+
This syntax simply removes the implicit `Sized` bound on every type parameter
71+
using the `?` symbol. It resolves the problem about not mentioning `Sized` that
72+
the first two syntaxes didn’t. It also hints towards being related to sizedness,
73+
resolving the problem that plagued `type`. It also successfully states that
74+
unsizedness is only *optional*—that the parameter may be sized or unsized. This
75+
syntax has stuck, and is the syntax used today. Additionally, it could
76+
potentially be extended to other traits: for example, a new pointer type that
77+
cannot be dropped, `&uninit`, could be added, requiring that it be written to
78+
before being dropped. However, many generic functions assume that any parameter
79+
passed to them can be dropped. `Drop` could be made a default bound to resolve
80+
this, and `Drop?` would remove this bound from a type parameter.
81+
82+
The problem with `Sized?`
83+
-------------------------
84+
85+
There is some inconsistency present with the `Sized` syntax. After going through
86+
multiple syntaxes for DST, all of which were keywords preceding type parameters,
87+
the `Sized?` annotation stayed *before* the type parameter’s name when it was
88+
adopted as the syntax for dynamically sized type parameters. This can be
89+
considered inconsistent in some ways—`Sized?` looks like a bound, contains a
90+
trait name like a bound does, and changes what types can unify with the type
91+
parameter like a bound does, but does not come *after* the type parameter’s name
92+
like a bound does. This also is inconsistent with Rust’s general pattern of not
93+
using C-style variable declarations (`int x`) but instead using a colon and
94+
placing the type after the name (`x: int`). (A type parameter is not strictly a
95+
variable declaration, but is similar: it declares a new name in a scope.) These
96+
problems together make `Sized?` the only marker that comes before type parameter
97+
or even variable names, and with the addition of negative bounds, it looks even
98+
more inconsistent:
99+
100+
```rust
101+
// Normal bound
102+
fn foo<T: Foo>() {}
103+
// Negative bound
104+
fn foo<T: !Foo>() {}
105+
// Generalising ‘anti-bound’
106+
fn foo<Foo? T>() {}
107+
```
108+
109+
The syntax also looks rather strange when recent features like associated types
110+
and `where` clauses are considered:
111+
112+
```rust
113+
// This `where` clause syntax doesn’t work today, but perhaps should:
114+
trait Foo<T> where Sized? T {
115+
type Sized? Bar;
116+
}
117+
```
118+
119+
Furthermore, another syntax had to be invented for traits for which `Self` can
120+
be unsized:
121+
122+
```rust
123+
trait Foo for Sized? { ... }
124+
```
125+
126+
This RFC proposes to change the syntax for dynamically sized type parameters to
127+
`T: Sized?` to resolve these issues.
128+
129+
Detailed design
130+
===============
131+
132+
Change the syntax for dynamically sized type parameters to `T: Sized?`:
133+
134+
```rust
135+
fn foo<T: Sized?>(x: &T) { ... }
136+
struct Foo<T: Send + Sized? + Sync> { field: Box<T> }
137+
trait Bar { type Baz: Sized?; }
138+
// etc.
139+
```
140+
141+
Change the syntax for traits for dynamically-sized types to look like the syntax
142+
for bounds on `Self`:
143+
144+
```rust
145+
trait Foo: Sized? { ... }
146+
```
147+
148+
Allow using this syntax in `where` clauses:
149+
150+
```rust
151+
fn foo<T>(x: &T) where T: Sized? { ... }
152+
```
153+
154+
Drawbacks
155+
=========
156+
157+
- The current syntax uses position to distinguish between removing and adding
158+
bounds, while the proposed syntax only uses a symbol. Since `Sized?` is
159+
actually an anti-bound (it removes a bound), it (in some ways) makes sense to
160+
put it on the opposite side of a type parameter to show this.
161+
162+
- Only a single character separates adding a `Sized` bound and removing an
163+
implicit one. This shouldn’t be a problem in general, as adding a `Sized`
164+
bound to a type parameter is pointless (because it is implicitly there
165+
already). A lint could be added to check for explicit default bounds if this
166+
turns out to be a problem.
167+
168+
Alternatives
169+
============
170+
171+
- Choose one of the previous syntaxes or a new syntax altogether. The drawbacks
172+
of the previous syntaxes are discussed in the ‘History of the DST syntax’
173+
section of this RFC.
174+
175+
Unresolved questions
176+
====================
177+
178+
None.

0 commit comments

Comments
 (0)