You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The variants of an enum are considered types in their own right, though they are necessarily
48
-
more restricted than most user-defined types. This means that when one define an enum, one is more
54
+
more restricted than most user-defined types. This means that when you define an enum, you are more
49
55
precisely defining a collection of types: the enumeration itself, as well as each of its
50
-
variants. However, the variant types act very similarly to the enum type in the majority of cases.
56
+
variants. However, the variant types act identically to the enum type in the majority of cases.
51
57
52
58
Specifically, variant types act differently to enum types in the following case:
53
59
- When pattern-matching on a variant type, only the constructor corresponding to the variant is
54
-
considered possible. Therefore one may irrefutably pattern-match on a variant.
60
+
considered possible. Therefore one may irrefutably pattern-match on a variant:
61
+
62
+
```rust
63
+
enumSum { A(u32), B, C }
64
+
65
+
fnprint_A(a:Sum::A) {
66
+
letA(x) =a;
67
+
println!("a is {}", a);
68
+
}
69
+
```
70
+
- One can project the fields of a variant type, similarly to tuples or structs:
71
+
72
+
```rust
73
+
fnprint_A(a:Sum::A) {
74
+
println!("a is {}", a.0);
75
+
}
76
+
```
55
77
56
78
Variant types, unlike most user-defined types are subject to the following restriction:
57
79
- Variant types may not have inherent impls, or implemented traits. That means `impl Enum::Variant`
58
80
and `impl Trait for Enum::Variant` are forbidden. This dissuades inclinations to implement
59
-
abstraction using behaviour-switching on enums, rather than using traits as is natural in Rust.
81
+
abstraction using behaviour-switching on enums (for example, by simulating inheritance-based
82
+
subtyping, with the enum type as the parent and each variant as children), rather than using traits
83
+
as is natural in Rust.
84
+
85
+
```rust
86
+
enumSum { A(u32), B, C }
87
+
88
+
implSum::A { // ERROR: variant types may not have specific implementations
89
+
// ...
90
+
}
91
+
```
92
+
93
+
```
94
+
error[E0XXX]: variant types may not have specific implementations
95
+
--> src/lib.rs:3:6
96
+
|
97
+
3 | impl Sum::A {
98
+
| ^^^^^^
99
+
| |
100
+
| `Sum::A` is a variant type
101
+
| help: you can try using the variant's enum: `Sum`
102
+
```
103
+
104
+
Variant types may be aliased with type aliases:
105
+
106
+
```rust
107
+
enumSum { A(u32), B, C }
60
108
61
-
Variant types may be aliased with type aliases.
109
+
typeSumA=Sum::A;
110
+
// `SumA` may now be used identically to `Sum::A`.
111
+
```
62
112
63
-
If a value of a variant type is explicitly cast to the type of its enum using a type annotation or
64
-
by passing it as an argument or return-value to or from a function, the variant information is lost
65
-
(that is, a variant type *is* different to an enum type, even though they behave very similarly).
113
+
If a value of a variant type is explicitly coerced or cast to the type of its enum using a type
114
+
annotation, `as`, or by passing it as an argument or return-value to or from a function, the variant
115
+
information is lost (that is, a variant type *is* different to an enum type, even though they behave
116
+
similarly).
66
117
67
-
Note that enum types may not be coerced to variant types. Instead, matching must be performed to
68
-
guarantee that the enum type truly is of the expected variant type.
118
+
Note that enum types may not be coerced or cast to variant types. Instead, matching must be
119
+
performed to guarantee that the enum type truly is of the expected variant type.
69
120
70
121
```rust
71
-
enumSum { A, B, C }
122
+
enumSum { A(u32), B, C }
72
123
73
124
lets:Sum=Sum::A;
74
125
75
126
leta=sasSum::A; // error
76
127
leta:Sum::A=s; // error
77
128
78
-
ifleta@Sum::A=s {
129
+
ifleta@Sum::A(_)=s {
79
130
// ok, `a` has type `Sum::A`
131
+
println!("a is {}", a.0);
80
132
}
81
133
```
82
134
135
+
Variant types interact as expected with the proposed
136
+
[generalised type ascription](https://github.com/rust-lang/rfcs/pull/2522) (i.e. the same as type
137
+
coercion in `let` or similar).
138
+
83
139
## Type parameters
84
140
Consider the following enum:
85
141
```rust
@@ -90,9 +146,11 @@ enum Either<A, B> {
90
146
```
91
147
Here, we are defining three types: `Either`, `Either::L` and `Either::R`. However, we have to be
92
148
careful here with regards to the type parameters. Specifically, the variants may not make use of
93
-
every generic paramter in the enum. Since variant types are generally considered simply as enum
149
+
every generic parameter in the enum. Since variant types are generally considered simply as enum
94
150
types, this means that the variants need all the type information of their enums, including all
95
-
their generic parameters.
151
+
their generic parameters. This explictness has the advantage of preserving variance for variant
152
+
types relative to their enum types, as well as permitting zero-cost coercions from variant types to
153
+
enum types.
96
154
97
155
So, in this case, we have the types: `Either<A, B>`, `Either<A, B>::L` and `Either::<A, B>::R`.
98
156
@@ -105,6 +163,10 @@ question. In most cases, the handling of `Variant` will simply delegate any beha
105
163
However, pattern-matching on the variant allows irrefutable matches on the particular variant. In
106
164
effect, `Variant` is only relevant to type checking/inference and the matching logic.
107
165
166
+
The discriminant of a `Variant` (as observed by [`discriminant_value`](https://doc.rust-lang.org/nightly/std/intrinsics/fn.discriminant_value.html)) is the discriminant
167
+
of the variant (i.e. identical to the value observed if the variant is first coerced to the enum
168
+
type).
169
+
108
170
Constructors of variants, as well as pattern-matching on particular enum variants, are now
109
171
inferred to have variant types, rather than enum types.
110
172
@@ -136,29 +198,42 @@ fn sum_match(s: Sum) {
136
198
In essence, a value of a variant is considered to be a value of the enclosing `enum` in every matter
137
199
but pattern-matching.
138
200
139
-
Explicitly casting to the `enum` type forgets the variant information.
201
+
Explicitly coercing or casting to the `enum` type forgets the variant information.
140
202
141
203
```rust
142
204
letx:Sum=Sum::A(5); // x: Sum
143
205
letSum::A(y) =x; // error: refutable match
206
+
207
+
letx=Sum::A(5) asSum; // x: Sum
208
+
letSum::A(y) =x; // error: refutable match
144
209
```
145
210
146
211
In all cases, the most specific type (i.e. the variant type if possible) is chosen by the type
147
212
inference. However, this is entirely backwards-compatible, because `Variant` acts as `Adt` except in
148
213
cases that were previously invalid (i.e. pattern-matching, where the extra typing information was
149
214
previously unknown).
150
215
216
+
Note that because a variant type, e.g. `Sum::A`, is not a subtype of the enum type (rather, it can
217
+
simply be coerced to the enum type), a type like `Vec<Sum::A>` is not a subtype of `Vec<Sum>`.
218
+
(However, this should not pose a problem as it should generally be convenient to coerce `Sum::A` to
219
+
`Sum` upon either formation or use.)
220
+
221
+
Note that we do not make any guarantees of the variant data representation at present, to allow us
222
+
flexibility to explore the design space.
223
+
151
224
# Drawbacks
152
225
[drawbacks]: #drawbacks
153
226
154
-
- The loose distinction between the `enum` type and its variant types could be confusing to those unfamiliar with variant types. Error messages might specifically mention a variant type, which could
227
+
- The loose distinction between the `enum` type and its variant types could be confusing to those
228
+
unfamiliar with variant types. Error messages might specifically mention a variant type, which could
155
229
be at odds with expectations. However, since they generally behave identically, this should not
156
230
prove to be a significant problem.
157
231
- As variant types need to include generic parameter information that is not necessarily included in
158
232
their definitions, it will be necessary to include explicit type annotations more often than is
159
233
typical. Although this is unfortunate, it is necessary to preserve all the desirable behaviour of
160
-
variant types described here: namely complete backwards-compatibility precise type inference
161
-
(e.g. allowing `x` in `let x = Sum::A;` to have type `Sum::A` without explicit type annotations).
234
+
variant types described here: namely complete backwards-compatibility precise type inference and
235
+
variance (e.g. allowing `x` in `let x = Sum::A;` to have type `Sum::A` without explicit type
One obvious alternative is to represent variant types differently to enum types and then coerce them
174
254
when used as an enum. This could potentially reduce memory overhead for smaller variants
175
255
(additionally no longer requiring the discriminant to be stored) and reduce the issue with providing
176
256
irrelevant type parameters. However, it makes coercion more expensive and complex (as a variant
177
-
could coerce to various enum types depending on the unspecified generic parameters).
257
+
could coerce to various enum types depending on the unspecified generic parameters). It is proposed
258
+
here that zero-cost coercions are more important. (In addition, simulating smaller variants is
259
+
possible by creating separate mirroring structs for each variant for which this is desired and
260
+
converting manually (though this is obviously not ideal), whereas simulating the proposed behaviour
261
+
with the alternative is much more difficult, if possible at all.)
178
262
179
263
Variant types have [previously been proposed for Rust](https://github.com/rust-lang/rfcs/pull/1450).
180
264
However, it used a more complex type inference procedure based on fallback and permitted fallible
@@ -193,9 +277,9 @@ However, it is often useful to briefly consider these variant types alone, which
193
277
RFC proposes.
194
278
195
279
Although sum types are becoming increasingly common in programming languages, most do not choose to
196
-
allow the variants to be treated as types in their own right. However, we propose that the patterns
197
-
in Rust make variant types more appealing than they might be in other programming languages with
198
-
variant types.
280
+
allow the variants to be treated as types in their own right (that is, the author has not found
281
+
any that permit this design pattern). However, we propose that the patterns in Rust make variant
282
+
types more appealing than they might be in other programming languages with variant types.
199
283
200
284
# Unresolved questions
201
285
[unresolved-questions]: #unresolved-questions
@@ -205,4 +289,7 @@ None.
205
289
# Future possibilities
206
290
[future-possibilities]: #future-possibilities
207
291
208
-
It would be possible to remove some of the restrictions on enum variant types in the future, such as permitting `impl`s or supporting variant types that don't contain all (irrelevant) generic parameters. This RFC has been written intentionally conservatively in this regard.
292
+
It would be possible to remove some of the restrictions on enum variant types in the future, such as
293
+
permitting `impl`s, supporting variant types that don't contain all (irrelevant) generic parameters
294
+
or permitting variant types to be subtypes of enum types. This RFC has been written intentionally
0 commit comments