Merge branch 'dst-syntax' of https://github.com/P1start/rfcs into P1start-dst-syntax

This commit is contained in:
Nick Cameron 2014-12-06 14:02:43 -08:00
commit aad8c9dc05
2 changed files with 182 additions and 0 deletions

View File

@ -56,6 +56,7 @@ the direction the language is evolving in.
* [0450-un-feature-gate-some-more-gates.md](text/0450-un-feature-gate-some-more-gates.md)
* [0459-disallow-shadowing.md](text/0459-disallow-shadowing.md)
* [0463-future-proof-literal-suffixes.md](text/0463-future-proof-literal-suffixes.md)
* [0490-dst-syntax.md](text/0490-dst-syntax.md)
## Table of Contents
[Table of Contents]: #table-of-contents

181
text/0490-dst-syntax.md Normal file
View File

@ -0,0 +1,181 @@
- Start Date: 2014-11-29
- RFC PR: [490](https://github.com/rust-lang/rfcs/pull/490)
- Rust Issue: [19607](https://github.com/rust-lang/rust/issues/19607)
Summary
=======
Change the syntax for dynamically sized type parameters from `Sized? T` to `T:
?Sized`, and change the syntax for traits for dynamically sized types to `trait
Foo for ?Sized`. Extend this new syntax to work with `where` clauses.
Motivation
==========
History of the DST syntax
-------------------------
When dynamically sized types were first designed, and even when they were first
being implemented, the syntax for dynamically sized type parameters had not been
fully settled on. Initially, dynamically sized type parameters were denoted by a
leading `unsized` keyword:
```rust
fn foo<unsized T>(x: &T) { ... }
struct Foo<unsized T> { field: T }
// etc.
```
This is the syntax used in Niko Matsakiss [initial design for
DST](http://smallcultfollowing.com/babysteps/blog/2014/01/05/dst-take-5/). This
syntax makes sense to those who are familiar with DST, but has some issues which
could be perceived as problems for those learning to work with dynamically sized
types:
- It implies that the parameter *must* be unsized, where really its only
optional;
- It does not visually relate to the `Sized` trait, which is fundamentally
related to declaring a type as unsized (removing the default `Sized` bound).
Later, Felix S. Klock II [came up with an alternative
syntax](http://blog.pnkfx.org/blog/2014/03/13/an-insight-regarding-dst-grammar-for-rust/)
using the `type` keyword:
```rust
fn foo<type T>(x: &T) { ... }
struct Foo<type T> { field: T }
// etc.
```
The inspiration behind this is that the union of all sized types and all unsized
types is simply all types. Thus, it makes sense for the most general type
parameter to be written as `type T`.
This syntax resolves the first problem listed above (i.e., it no longer implies
that the type *must* be unsized), but does not resolve the second. Additionally,
it is possible that some people could be confused by the use of the `type`
keyword, as it contains little meaning—one would assume a bare `T` as a *type*
parameter to be a type already, so what does adding a `type` keyword mean?
Perhaps because of these concerns, the syntax for dynamically sized type
parameters has since been changed one more time, this time to use the `Sized`
traits name followed by a question mark:
```rust
fn foo<Sized? T>(x: &T) { ... }
struct Foo<Sized? T> { field: T }
// etc.
```
This syntax simply removes the implicit `Sized` bound on every type parameter
using the `?` symbol. It resolves the problem about not mentioning `Sized` that
the first two syntaxes didnt. It also hints towards being related to sizedness,
resolving the problem that plagued `type`. It also successfully states that
unsizedness is only *optional*—that the parameter may be sized or unsized. This
syntax has stuck, and is the syntax used today. Additionally, it could
potentially be extended to other traits: for example, a new pointer type that
cannot be dropped, `&uninit`, could be added, requiring that it be written to
before being dropped. However, many generic functions assume that any parameter
passed to them can be dropped. `Drop` could be made a default bound to resolve
this, and `Drop?` would remove this bound from a type parameter.
The problem with `Sized? T`
---------------------------
There is some inconsistency present with the `Sized` syntax. After going through
multiple syntaxes for DST, all of which were keywords preceding type parameters,
the `Sized?` annotation stayed *before* the type parameters name when it was
adopted as the syntax for dynamically sized type parameters. This can be
considered inconsistent in some ways—`Sized?` looks like a bound, contains a
trait name like a bound does, and changes what types can unify with the type
parameter like a bound does, but does not come *after* the type parameters name
like a bound does. This also is inconsistent with Rusts general pattern of not
using C-style variable declarations (`int x`) but instead using a colon and
placing the type after the name (`x: int`). (A type parameter is not strictly a
variable declaration, but is similar: it declares a new name in a scope.) These
problems together make `Sized?` the only marker that comes before type parameter
or even variable names, and with the addition of negative bounds, it looks even
more inconsistent:
```rust
// Normal bound
fn foo<T: Foo>() {}
// Negative bound
fn foo<T: !Foo>() {}
// Generalising anti-bound
fn foo<Foo? T>() {}
```
The syntax also looks rather strange when recent features like associated types
and `where` clauses are considered:
```rust
// This `where` clause syntax doesnt work today, but perhaps should:
trait Foo<T> where Sized? T {
type Sized? Bar;
}
```
Furthermore, the `?` on `Sized?` comes after the trait name, whereas most
unary-operator-like symbols in the Rust language come before what they are
attached to.
This RFC proposes to change the syntax for dynamically sized type parameters to
`T: ?Sized` to resolve these issues.
Detailed design
===============
Change the syntax for dynamically sized type parameters to `T: ?Sized`:
```rust
fn foo<T: ?Sized>(x: &T) { ... }
struct Foo<T: Send + ?Sized + Sync> { field: Box<T> }
trait Bar { type Baz: ?Sized; }
// etc.
```
Change the syntax for traits for dynamically-sized types to have a prefix `?`
instead of a postfix one:
```rust
trait Foo for ?Sized { ... }
```
Allow using this syntax in `where` clauses:
```rust
fn foo<T>(x: &T) where T: ?Sized { ... }
```
Drawbacks
=========
- The current syntax uses position to distinguish between removing and adding
bounds, while the proposed syntax only uses a symbol. Since `?Sized` is
actually an anti-bound (it removes a bound), it (in some ways) makes sense to
put it on the opposite side of a type parameter to show this.
- Only a single character separates adding a `Sized` bound and removing an
implicit one. This shouldnt be a problem in general, as adding a `Sized`
bound to a type parameter is pointless (because it is implicitly there
already). A lint could be added to check for explicit default bounds if this
turns out to be a problem.
Alternatives
============
- Choose one of the previous syntaxes or a new syntax altogether. The drawbacks
of the previous syntaxes are discussed in the History of the DST syntax
section of this RFC.
- Change the syntax to `T: Sized?` instead. This is less consistent with things
like negative bounds (which would probably be something like `T: !Foo`), and
uses a suffix operator, which is less consistent with other parts of Rusts
syntax. It is, however, closer to the current syntax (`Sized? T`), and looks
more natural because of how `?` is used in natural languages such as English.
Unresolved questions
====================
None.