Skip to content

Commit 05e10ca

Browse files
committed
Remove ascription on patterns. Add details about coercions.
1 parent 32eadf4 commit 05e10ca

File tree

1 file changed

+43
-111
lines changed

1 file changed

+43
-111
lines changed

text/0000-type-ascription.md

Lines changed: 43 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44

55
# Summary
66

7-
Add type ascription to expressions and patterns.
7+
Add type ascription to expressions. (An earlier version of this RFC covered type
8+
ascription in patterns too, that has been postponed).
89

9-
Type ascription on expression has already been implemented. Type ascription on
10-
patterns can probably wait until post-1.0.
10+
Type ascription on expression has already been implemented.
1111

1212
See also discussion on [#354](https://github.com/rust-lang/rfcs/issues/354) and
1313
[rust issue 10502](https://github.com/rust-lang/rust/issues/10502).
@@ -16,12 +16,12 @@ See also discussion on [#354](https://github.com/rust-lang/rfcs/issues/354) and
1616
# Motivation
1717

1818
Type inference is imperfect. It is often useful to help type inference by
19-
annotating a sub-expression or sub-pattern with a type. Currently, this is only
20-
possible by extracting the sub-expression into a variable using a `let`
21-
statement and/or giving a type for a whole expression or pattern. This is un-
22-
ergonomic, and sometimes impossible due to lifetime issues. Specifically, a
23-
variable has lifetime of its enclosing scope, but a sub-expression's lifetime is
24-
typically limited to the nearest semi-colon.
19+
annotating a sub-expression with a type. Currently, this is only possible by
20+
extracting the sub-expression into a variable using a `let` statement and/or
21+
giving a type for a whole expression or pattern. This is un- ergonomic, and
22+
sometimes impossible due to lifetime issues. Specifically, where a variable has
23+
lifetime of its enclosing scope, but a sub-expression's lifetime is typically
24+
limited to the nearest semi-colon.
2525

2626
Typical use cases are where a function's return type is generic (e.g., collect)
2727
and where we want to force a coercion.
@@ -90,18 +90,6 @@ let x: T = {
9090
let x: T foo(): U<_>;
9191
```
9292

93-
In patterns:
94-
95-
```
96-
struct Foo<T> { a: T, b: String }
97-
98-
// Current
99-
fn foo(Foo { a, .. }: Foo<i32>) { ... }
100-
101-
// With type ascription.
102-
fn foo(Foo { a: i32, .. }) { ... }
103-
```
104-
10593

10694
# Detailed design
10795

@@ -122,72 +110,44 @@ At runtime, type ascription is a no-op, unless an implicit coercion was used in
122110
type checking, in which case the dynamic semantics of a type ascription
123111
expression are exactly those of the implicit coercion.
124112

125-
The syntax of patterns is extended to include an optional type ascription.
126-
Old syntax:
127-
128-
```
129-
PT ::= P: T
130-
P ::= var | 'box' P | ...
131-
e ::= 'let' (PT | P) = ... | ...
132-
```
133-
134-
where `PT` is a pattern with optional type, `P` is a sub-pattern, `T` is a type,
135-
and `var` is a variable name. (Formal arguments are `PT`, patterns in match arms
136-
are `P`).
113+
@eddyb has implemented the expressions part of this RFC,
114+
[PR](https://github.com/rust-lang/rust/pull/21836).
137115

138-
New syntax:
139116

140-
```
141-
PT ::= P: T | P
142-
P ::= var | 'box' PT | ...
143-
e ::= 'let' PT = ... | ...
144-
```
117+
### coercion and `as` vs `:`
145118

146-
Type ascription in patterns has the narrowest precedence, e.g., `box x: T` means
147-
`box (x: T)`. In particular, in a struct initialiser or patter, `x : y : z` is
148-
parsed as `x : (y: z)`, i.e., a field named `x` is initialised with a value `y`
149-
and that value must have type `z`. If only `x: y` is given, that is considered
150-
to be the field name and the field's contents, with no type ascription.
119+
A downside of type ascription is the overlap with explicit coercions (aka casts,
120+
the `as` operator). Type ascription makes implicit coercions explicit. In RFC
121+
401, it is proposed that all valid implicit coercions are valid explicit
122+
coercions. However, that may be too confusing for users, since there is no
123+
reason to use type ascription rather than `as` (if there is some coercion).
124+
Furthermore, if programmers do opt to use `as` as the default whether or not it
125+
is required, then it loses its function as a warning sign for programmers to
126+
beware of.
151127

152-
The chagnes to pattern syntax mean that in some contexts where a pattern
153-
previously required a type annotation, it is no longer required if all variables
154-
can be assigned types via the ascription. Examples,
128+
To address this I propose three lints which check for: trivial casts, coercible
129+
casts, and trivial numeric casts. Other than these lints we stick with the
130+
proposal from #401 that unnecessary casts will no longer be an error.
155131

156-
```
157-
struct Foo {
158-
a: Bar,
159-
b: Baz,
160-
}
161-
fn foo(x: Foo); // Ok, type of x given by type of whole pattern
162-
fn foo(Foo { a: x, b: y}: Foo) // Ok, types of x and y found by destructuring
163-
fn foo(Foo { a: x: Bar, b: y: Baz}) // Ok, no type annotation, but types given as ascriptions
164-
fn foo(Foo { a: x: Bar, _ }) // Ok, we can still deduce the type of x and the whole argument
165-
fn foo(Foo { a: x, b: y}) // Ok, type of x and y given by Foo
166-
167-
struct Qux<X> {
168-
a: Bar,
169-
b: X,
170-
}
171-
fn foo(x: Qux<Baz>); // Ok, type of x given by type of whole pattern
172-
fn foo(Qux { a: x, b: y}: Qux<Baz>) // Ok, types of x and y found by destructuring
173-
fn foo(Qux { a: x: Bar, b: y: Baz}) // Ok, no type annotation, but types given as ascriptions
174-
fn foo(Qux { a: x: Bar, _ }) // Error, can't find the type of the whole argument
175-
fn foo(Qux { a: x, b: y}) // Error can't find type of y or the whole argument
176-
```
132+
A trivial cast is a cast `x as T` where `x` has type `U` and `U` is a subtype of
133+
`T` (note that subtyping includes reflexivity).
177134

178-
Note the above changes mean moving some errors from parsing to later in type
179-
checking. For example, all uses of patterns have optional types, and it is a
180-
type error if there must be a type (e.g., in function arguments) but it is not
181-
fully specified (currently it would be a parsing error).
135+
A coercible cast is a cast `x as T` where `x` has type `U` and `x` can be
136+
implicitly coerced to `T`, but `U` is not a subtype of `T`.
182137

183-
In type checking, if an expression is matched against a pattern, when matching
184-
a sub-pattern the matching sub-expression must have the ascribed type (again,
185-
this check includes subtyping and implicit coercion). Types in patterns play no
186-
role at runtime.
138+
A trivial numeric cast is a cast `x as T` where `x` has type `U` and `x` is
139+
implicitly coercible to `T` or `U` is a subtype of `T`, and both `U` and `T` are
140+
numeric types.
187141

188-
@eddyb has implemented the expressions part of this RFC,
189-
[PR](https://github.com/rust-lang/rust/pull/21836).
142+
Like any lints, these can be customised per-crate by the programmer. The trivial
143+
cast lint is 'deny' by default (i.e., causes an error); the coercible cast and
144+
trivial numeric cast lints are 'warn' by default.
190145

146+
Although this is a somewhat complex scheme, it allows code that works today to
147+
work with only minor adjustment, it allows for a backwards compatible path to
148+
'promoting' type conversions from explicit casts to implicit coercions, and it
149+
allows customisation of a contentious kind of error (especially so in the
150+
context of cross-platform programming).
191151

192152
# Drawbacks
193153

@@ -205,11 +165,6 @@ difficult to support the same syntax as field initialisers.
205165

206166
We could do nothing and force programmers to use temporary variables to specify
207167
a type. However, this is less ergonomic and has problems with scopes/lifetimes.
208-
Patterns can be given a type as a whole rather than annotating a part of the
209-
pattern.
210-
211-
We could allow type ascription in expressions but not patterns. This is a
212-
smaller change and addresses most of the motivation.
213168

214169
Rely on explicit coercions - the current plan [RFC 401](https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md)
215170
is to allow explicit coercion to any valid type and to use a customisable lint
@@ -221,35 +176,12 @@ which require more programmer attention. This also does not help with patterns.
221176

222177
We could use a different symbol or keyword instead of `:`, e.g., `is`.
223178

224-
# Unresolved questions
225-
226-
Is the suggested precedence correct? Especially for patterns.
227179

228-
Does type ascription on patterns have backwards compatibility issues?
229-
230-
Given the potential confusion with struct literal syntax, it is perhaps worth
231-
re-opening that discussion. But given the timing, probably not.
180+
# Unresolved questions
232181

233-
Should remove integer suffixes in favour of type ascription?
182+
Is the suggested precedence correct?
234183

235-
### `as` vs `:`
184+
Should we remove integer suffixes in favour of type ascription?
236185

237-
A downside of type ascription is the overlap with explicit coercions (aka casts,
238-
the `as` operator). Type ascription makes implicit coercions explicit. In RFC
239-
401, it is proposed that all valid implicit coercions are valid explicit
240-
coercions. However, that may be too confusing for users, since there is no
241-
reason to use type ascription rather than `as` (if there is some coercion). It
242-
might be a good idea to revisit that decision (it has not yet been implemented).
243-
Then it is clear that the user uses `as` for explicit casts and `:` for non-
244-
coercing ascription and implicit casts. Although there is no hard guideline for
245-
which operations are implicit or explicit, the intuition is that if the
246-
programmer ought to be aware of the change (i.e., the invariants of using the
247-
type change to become less safe in any way) then coercion should be explicit,
248-
otherwise it can be implicit.
249-
250-
Alternatively we could remove `as` and require `:` for explicit coercions, but
251-
not for implicit ones (they would keep the same rules as they currently have).
252-
The only loss would be that `:` doesn't stand out as much as `as` and there
253-
would be no lint for trivial coercions. Another (backwards compatible)
254-
alternative would be to keep `as` and `:` as synonyms and recommend against
255-
using `as`.
186+
Style guidelines - should we recommend spacing or parenthesis to make type
187+
ascription syntax more easily recognisable?

0 commit comments

Comments
 (0)