-
Notifications
You must be signed in to change notification settings - Fork 48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enum Variant Types #122
Comments
This issue is not meant to be used for technical discussion. There is a Zulip stream for that. Use this issue to leave procedural comments, such as volunteering to review, indicating that you second the proposal (or third, etc), or raising a concern that you would like to be addressed. |
Coming from Kotlin most of the time it is a pain to use enums in Rust. enum MyEnum {
A(u32),
B { x: u32 },
}
fn print_A(a: MyEnum::A) {
println!("a is {}", a.0);
}
fn print_B(a: MyEnum::B) {
println!("b is {}", b.x);
}
fn print_no_pattern(e: MyEnum) {
match e {
MyEnum::A => println!("e is {}", e.0); // kotlin like smart casts, no need to introduce new variables
MyEnum::B => println!("e is {}", e.x);
}
}
fn print_if_A(e: MyEnum) {
if e == MyEnum::A {
println!("a is {}", a.0);
}
} Enums are basically unions of structs or tuples. Just allow to use them as they are. Sometimes pattern matching is way too verbose while not providing any real benefit. Just as an example pattern matching with enums with 4+ fields becomes a nightmare, especially if you only need to know a type. Also when you have to pass specific enum variant in a chain of methods - you need to add pattern matching in each of those with panics otherwise. This reduces "code is documentation" and other developers might pass values that are not expected. Yes, you can create struct for each enum variant but this removes a lot of conciseness of the enums and requires a lot of code for somewhat basic requirement. |
Hi! We discussed this in a number of recent @rust-lang/lang meetings (2021-11-02, 2021-11-03). Based on those meetings, we decided that we are going to close this issue, but not without a good helping of regret. The bottom line is this: we all agree that it is a common, and annoying, pattern in Rust today to have to make a struct for every enum variant and then just have the enum wrap those structs. This gives you the ability to have a "type for an enum variant", but is annoying and inconvenient. So, for those reasons, we would love to see forward motion on this proposal. However, we also feel that this is striking at a fairly core part of the language, and there isn't anyone on the team who has the bandwidth to actively liaison this effort. We've had the experience (cough never type cough) of accepting fundamental extensions to the language without dedicating real bandwidth to implementing and supporting them, and it is not good. So we are trying to do better on that score. One other concern that was raised is that there are a lot of "nice to haves" here -- for example, it might be nice if One possibility might be using a procedural macro to try and prototype some of the ideas, though I haven't invested much thought into how that might work. I'm going to tag this as "final comment period", feel free to respond to any of the points above; the team can discuss again next week and decide whether or not to truly close. |
This is a good one and I'd really love to see it implemented. PS: just don't add more "matches!"-like macros for enums. IMHO it really looks like a half-baked solution/workaround. |
Closing as discussed in previous comment. |
You could have a look at desugar. |
Proposal
Summary and problem statement
(taken from: rust-lang/rfcs#2593)
Consider enum variants types in their own rights. This allows them to be irrefutably matched
upon. Where possible, type inference will infer variant types, but as variant types may always be
treated as enum types this does not cause any issues with backwards-compatibility.
Motivation, use-cases, and solution sketches
When working with enums, it is frequently the case that some branches of code have assurance that
they are handling a particular variant of the enum (1, 2, 3, 4, 5, etc.). This is especially the case when abstracting
behaviour for a certain enum variant. However, currently, this information is entirely hidden to the
compiler and so the enum types must be matched upon even when the variant is certainly known.
By treating enum variants as types in their own right, this kind of abstraction is made cleaner,
avoiding the need for code patterns such as:
unreachable!()
arms for the othervariants.
struct
.However, though abstracting behaviour for specific variants is often convenient, it is understood
that such variants are intended to be treated as enums in general. As such, the variant types
proposed here have identical representations to their enums; the extra type information is simply
used for type checking and permitting irrefutable matches on enum variants.
Guide-level explanation
The variants of an enum are considered types in their own right, though they are necessarily
more restricted than most user-defined types. This means that when you define an enum, you are more
precisely defining a collection of types: the enumeration itself, as well as each of its
variants. However, the variant types act identically to the enum type in the majority of cases.
Specifically, variant types act differently to enum types in the following case:
considered possible. Therefore you may irrefutably pattern-match on a variant:
However, to be backwards-compatible with existing handling of variants as enums, matches on
variant types will permit (and simply ignore) arms that correspond to other variants:
To avoid this behaviour, a new lint,
strict_variant_matching
will be added that will forbidmatching on other variants.
Variant types, unlike most user-defined types are subject to the following restriction:
impl Enum::Variant
and
impl Trait for Enum::Variant
are forbidden. This dissuades inclinations to implementabstraction using behaviour-switching on enums (for example, by simulating inheritance-based
subtyping, with the enum type as the parent and each variant as children), rather than using traits
as is natural in Rust.
Variant types may be aliased with type aliases:
If a value of a variant type is explicitly coerced or cast to the type of its enum using a type
annotation,
as
, or by passing it as an argument or return-value to or from a function, the variantinformation is lost (that is, a variant type is different to an enum type, even though they behave
similarly).
Note that enum types may not be coerced or cast to variant types. Instead, matching must be
performed to guarantee that the enum type truly is of the expected variant type.
If multiple variants are bound with a single binding variable
x
, then the type ofx
will simplybe the type of the enum, as before (i.e. binding on variants must be unambiguous).
Variant types interact as expected with the proposed
generalised type ascription (i.e. the same as type
coercion in
let
or similar).Type parameters
Consider the following enum:
Here, we are defining three types:
Either
,Either::L
andEither::R
. However, we have to becareful here with regards to the type parameters. Specifically, the variants may not make use of
every generic parameter in the enum. Since variant types are generally considered simply as enum
types, this means that the variants need all the type information of their enums, including all
their generic parameters. This explictness has the advantage of preserving variance for variant
types relative to their enum types, as well as permitting zero-cost coercions from variant types to
enum types.
So, in this case, we have the types:
Either<A, B>
,Either<A, B>::L
andEither::<A, B>::R
.Links and related work
The text was updated successfully, but these errors were encountered: