Currently, the solution to including existing structs and tuple-structs as variants of an enum is to wrap it in a tag, as with any other variant. I believe this is sub-optimal for reasons of ergonomics, brevity, and clarity, however. e.g. when constructing or pattern matching on such values, one ends up doing things like Foo::Bar(Bar(...))
, where the duplication of Bar
is theoretically unnecessary.
For this reason, I prepose the following syntax:
struct Foo { ... }
struct Bar(...)
type Baz = Bar;
enum A {
type Foo,
type Bar, // specifying `type Bar` again yields an error
type Baz, // specifying `type Baz` again yields an error
}
// Construct a value of type `A`.
let a1 = A::Foo { ... };
let a2 = A::Bar(...);
let a3 = A::Baz(...);
// Pattern matching a value of type `A` with destructuring.
match a {
A::Foo { ... } => ...,
A::Bar(...) => ...,
A::Baz(...) => ...,
}
// Pattern matching a value of type `A` with binding.
match a {
A::Foo @ foo => ..., // `foo` is of type `Foo`
A::Bar @ bar => ..., // `bar` is of type `Bar`
A::Baz @ baz => ..., // `baz` is of type `Baz` == `Bar`
}
// If-let pattern matching with destructuring.
if let A::Foo { ... } = a { ... }
// If-let pattern matching with binding.
if let A::Foo @ foo = a { ... } // `foo` is of type `Foo`
which should be equivalent to the following in today’s Rust:
struct Foo { ... }
struct Bar(...)
type Baz = Bar;
enum A {
Foo(Foo),
Bar(Bar),
Baz(Baz),
}
// Construct a value of type `A`.
let a1 = A::Foo(Foo { ... });
let a2 = A::Bar(Bar(...));
let a3 = A::Baz(Baz(...));
// Pattern matching a value of type `A` with destructuring.
match a {
A::Foo(Foo { ... }) => ...,
A::Bar(Bar(...)) => ...,
A::Baz(Baz(...)) => ...,
}
// Pattern matching a value of type `A` with binding.
match a {
A::Foo(foo) => ..., // `foo` is of type `Foo`
A::Bar(bar) => ..., // `bar` is of type `Bar`
A::Baz(baz) => ..., // `baz` is of type `Baz` == `Bar`
}
// If-let pattern matching with destructuring.
if let A::Foo(Foo { ... }) = a { ... }
// If-let pattern matching with binding.
if let A::Foo(foo) = a { ... } // `foo` is of type `Foo`
Unfortunately, enum variants are not presently treated as actual types. (I believe this has gone through the RFC stage before but not developed, for whatever reason.) While that proposal would mesh nicely with this one, it is not strictly necessary, and as should be clear from the above, this feature can be implemented purely as syntactical sugar for tagging the variant type with a name identical to the type (or type alias’s) name, as is the common practice today.
As for concrete use cases, one example of where I believe it could really cut down on boilerplate and improve clarity is in the syn crate (and likewise other code involving parsers and expression trees). See this file for example. Additionally, I think this feature would encourage the reuse of variants between various enums, and make it far more ergonomic to do.
Note: this proposal was discussed briefly on IRC with @Centril and @mbrubeck, who suggested it could work in principle. The only possible issue implementation issue we raised was memory alignment of direct vs. indirect struct & tuple-struct variants, but this would probably not be a major one.