4
4
5
5
# Summary
6
6
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).
8
9
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.
11
11
12
12
See also discussion on [ #354 ] ( https://github.com/rust-lang/rfcs/issues/354 ) and
13
13
[ 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
16
16
# Motivation
17
17
18
18
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.
25
25
26
26
Typical use cases are where a function's return type is generic (e.g., collect)
27
27
and where we want to force a coercion.
@@ -90,18 +90,6 @@ let x: T = {
90
90
let x: T foo(): U<_>;
91
91
```
92
92
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
-
105
93
106
94
# Detailed design
107
95
@@ -122,72 +110,44 @@ At runtime, type ascription is a no-op, unless an implicit coercion was used in
122
110
type checking, in which case the dynamic semantics of a type ascription
123
111
expression are exactly those of the implicit coercion.
124
112
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 ) .
137
115
138
- New syntax:
139
116
140
- ```
141
- PT ::= P: T | P
142
- P ::= var | 'box' PT | ...
143
- e ::= 'let' PT = ... | ...
144
- ```
117
+ ### coercion and ` as ` vs ` : `
145
118
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.
151
127
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.
155
131
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).
177
134
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 ` .
182
137
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.
187
141
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.
190
145
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).
191
151
192
152
# Drawbacks
193
153
@@ -205,11 +165,6 @@ difficult to support the same syntax as field initialisers.
205
165
206
166
We could do nothing and force programmers to use temporary variables to specify
207
167
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.
213
168
214
169
Rely on explicit coercions - the current plan [ RFC 401] ( https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md )
215
170
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.
221
176
222
177
We could use a different symbol or keyword instead of ` : ` , e.g., ` is ` .
223
178
224
- # Unresolved questions
225
-
226
- Is the suggested precedence correct? Especially for patterns.
227
179
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
232
181
233
- Should remove integer suffixes in favour of type ascription ?
182
+ Is the suggested precedence correct ?
234
183
235
- ### ` as ` vs ` : `
184
+ Should we remove integer suffixes in favour of type ascription?
236
185
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