5
5
# Summary
6
6
7
7
The variants of an enum are currently defined in the same namespace as the enum
8
- itself. This RFC proposes to define variants under the enum's namespace. Since
9
- 1.0 is fast approaching, this RFC outlines a path which requires a single,
10
- small, change before 1.0 that allows for the remainder to be implemented after
11
- 1.0 while preserving backwards compatibility.
8
+ itself. This RFC proposes to define variants under the enum's namespace.
12
9
13
10
## Note
14
11
@@ -230,147 +227,30 @@ use messages::{FrontendMessage, BackendMessage, WriteMessage, ReadMessage};
230
227
231
228
# Detailed design
232
229
233
- The implementation is split into several stages. Only the first needs to be
234
- implemented before 1.0.
235
-
236
- ## Before 1.0
237
-
238
- To ensure that the future changes preserve backwards compatibility, we must
239
- add a small restriction: an inherent ` impl ` for an enum may not define any
240
- methods with the same name as any of the enum's variants. For example, both
241
- methods defined for ` Foo ` would be forbidden:
242
- ``` rust
243
- enum Foo {
244
- Bar ,
245
- Baz ,
246
- }
247
-
248
- impl Foo {
249
- fn Bar (& self ) {}
250
- fn Baz () {}
251
- }
252
- ```
253
- This should not affect much if any code in practice, as Rust's naming
254
- conventions state that variants should be capitalized and method names should
255
- not.
256
-
257
- ## With other post-1.0 work
258
-
259
- Part of the UFCS work will (or at least discussed) allowing this:
260
- ```
261
- trait Foo {
262
- fn Baz();
263
- }
264
-
265
- enum Bar {
266
- Baz,
267
- }
268
-
269
- impl Foo for Bar {
270
- fn Baz() {}
271
- }
272
-
273
- fn main() {
274
- Bar::Baz(); // instead of Foo::<for Bar>::Baz()
275
- }
276
- ```
277
- If this is implemented before namespaced enums, we must add a restriction,
278
- similar to the one for intrinsic methods, that prohibits something like
279
- ` Foo::Baz ` from being called as in the above example. Note that the trait may
280
- be implemented for ` Bar ` without any problems, and the method may be called via
281
- the long form. It's just ` Bar::Baz() ` that must be disallowed.
282
-
283
- ## Later
284
-
285
- The compiler's resolve stage will be altered in two ways. It will place an
286
- enum's variants into the enum's sub-namespace in a similar way to methods
287
- defined in inherent ` impl ` s. Note that there is one key difference between
288
- those methods and the variants: the methods cannot be ` use ` d, while the
289
- variants can be.
290
-
291
- In addition, when searching for a name in some namespace and the name is not
292
- present, the compiler will search all enums defined in that namespace. If
293
- exactly one variant with the proper name is found, the name will resolve to
294
- that variant. If there are multiple variants with the same name, an error will
295
- be raised (It isn't possible to end up in this situation in code written before
296
- this is implemented, but will be possible in code written after). This ensures
297
- that the namespacing change is fully backwards compatible. This behavior is
298
- more conservative than it needs to be - it could try to use type inference to
299
- determine which of the multiple variants should be selected, for example.
300
- However, the fallback is designed to be just that - a fallback to make sure
301
- that code does not break in the change. It is not intended to be depended on by
302
- code written after the change has been made.
303
-
304
- ### Examples
305
-
306
- ``` rust
307
- enum Foo {
308
- Bar ,
309
- Baz ,
310
- Buz ,
311
- }
312
-
313
- fn ok (f : Foo ) {
314
- use Foo :: Buz ;
315
-
316
- match f {
317
- Bar => {} // backwards compatibility fallback
318
- Foo :: Baz => {}
319
- Buz => {}
320
- }
321
- }
322
- ```
323
-
324
- ``` rust
325
- enum Fruit {
326
- Apple ,
327
- Orange ,
328
- }
329
-
330
- enum Color {
331
- Red ,
332
- Orange ,
333
- }
334
-
335
- fn broken (f : Fruit ) {
336
- match f {
337
- Apple => {} // fallback
338
- Orange => {} // ~ ERROR ambiguous reference to a variant
339
- }
340
- }
341
-
342
- fn ok (f : Fruit ) {
343
- use Fruit :: {Apple , Orange };
344
- match f {
345
- Apple => {}
346
- Orange => {}
347
- }
348
- }
349
- ```
350
-
351
- ``` rust
352
- enum Foo {
353
- Bar ,
354
- Baz
355
- }
356
-
357
- struct Bar ;
358
-
359
- fn broken (f : Foo ) {
360
- match f {
361
- Bar => {} // ~ ERROR expected a `Foo` but found type `Bar`
362
- Baz => {}
363
- }
364
- }
365
- ```
366
-
367
- In addition, we can add a default-allow lint that identifies use of the
368
- compatibility fallback. This will allow developers to minimize the chance of
369
- breakage down the line if we decide to remove this fallback in Rust 2.0 as well
370
- as avoid code that will presumably end up being considered bad style. Note that
371
- this would not trigger if the variants are explicitly reexported in the module,
372
- so enums that are designed to be used in the current manner will not be
373
- affected.
230
+ The compiler's resolve stage will be altered to place the value and type
231
+ definitions for variants in their enum's module, just as methods of inherent
232
+ impls are. Variants will be handled differently than those methods are,
233
+ however. Methods cannot currently be directly imported via ` use ` , while
234
+ variants will be. The determination of importability currently happens at the
235
+ module level. This logic will be adjusted to move that determination to the
236
+ definition level. Specifically, each definition will track its "importability",
237
+ just as it currently tracks its "publicness". All definitions will be
238
+ importable except for methods in implementations and trait declarations.
239
+
240
+ The implementation will happen in two stages. In the first stage, resolve will
241
+ be altered as described above. However, variants will be defined in * both* the
242
+ flat namespace and nested namespace. This is necessary t keep the compiler
243
+ bootstrapping.
244
+
245
+ After a new stage 0 snapshot, the standard library will be ported and resolve
246
+ will be updated to remove variant definitions in the flat namespace. This will
247
+ happen as one atomic PR to keep the implementation phase as compressed as
248
+ possible. In addition, if unforseen problems arise during this set of work, we
249
+ can roll back the initial commit and put the change off until after 1.0, with
250
+ only a small pre-1.0 change required. This initial conversion will focus on
251
+ making the minimal set of changes required to port the compiler and standard
252
+ libraries by reexporting variants in the old location. Later work can alter
253
+ the APIs to take advantage of the new definition locations.
374
254
375
255
## Library changes
376
256
@@ -392,28 +272,37 @@ pub enum Item {
392
272
Static (Bar ),
393
273
}
394
274
```
395
- In the Rust standard library, this can be used to update libraries to a cleaner
396
- API without breaking backwards compatibility if the library changes are made in
397
- the same release as the language change.
275
+ To simply keep existing code compiling, a glob reexport will suffice:
276
+ ``` rust
277
+ pub use Item :: * ;
398
278
399
- # Drawbacks
279
+ pub enum Item {
280
+ ItemStruct (Foo ),
281
+ ItemStatic (Bar ),
282
+ }
283
+ ```
284
+ Once RFC #385 is implemented, it will be possible to write a syntax extension
285
+ that will automatically generate the reexport:
286
+ ``` rust
287
+ #[flatten]
288
+ pub enum Item {
289
+ ItemStruct (Foo ),
290
+ ItemStatic (Bar ),
291
+ }
292
+ ```
400
293
401
- This will cause a period of uncertainty in libraries, where libraries that
402
- aren't update become unidiomatic and libraries that are updated accumulate
403
- cruft if they attempt to maintain backwards compatibility.
294
+ # Drawbacks
404
295
405
- The compatibility fallback logic will add complexity to resolve, and with
406
- increased complexity comes an increased chance of bugs .
296
+ The transition period will cause enormous breakage in downstream code. It is
297
+ also a fairly large change to make to resolve, which is already a bit fragile .
407
298
408
299
# Alternatives
409
300
410
- We can push to switch entirely to namespaced enums before 1.0. This will allow
411
- us to avoid the compatibility fallback and minimize library cruft, at the cost
412
- of adding a significant amount of work to the 1.0 docket. If we decide to go
413
- down this route, we could use the same implementation strategy laid out here to
414
- avoid breaking the entire world at once - implement the fallback and lint at a
415
- default (or forced) ` warn ` level and then remove both after some period of
416
- time.
301
+ We can implement enum namespacing after 1.0 by adding a "fallback" case to
302
+ resolve, where variants can be referenced from their "flat" definition location
303
+ if no other definition would conflict in that namespace. In the grand scheme of
304
+ hacks to preserve backwards compatibility, this is not that bad, but still
305
+ decidedly worse than not having to worry about fallback at all.
417
306
418
307
Earlier iterations of namespaced enum proposals suggested preserving flat enums
419
308
and adding ` enum mod ` syntax for namespaced enums. However, variant namespacing
0 commit comments