Skip to content
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

Type alias enum variants #2338

Merged
merged 2 commits into from
Apr 5, 2018
Merged

Conversation

djc
Copy link
Contributor

@djc djc commented Feb 15, 2018

This RFC proposes to allow access to enum variants through type aliases. This enables better abstraction/information hiding by encapsulating enums in aliases without having to create another enum type and requiring the conversion from and into the "alias" enum.

@scottmcm scottmcm added the T-lang Relevant to the language team, which will review and decide on the RFC. label Feb 15, 2018
@WiSaGaN
Copy link
Contributor

WiSaGaN commented Feb 19, 2018

I have run into this in my projects several times. Fortunately, I have access to the original type definition, so I could add a "constructor" from variant to work around this. The absence of this functionality came as a surprise to me, although it is not a bug in a sense. Implementing this would reduce the frustration people experience when initially they implicitly thought it would work, but later found out it would not.

Another question is how close this relates to using alias to construct unit and tuple. From a usage point of view, they are the same surprising unsupported functionality of alias. Do they differ a lot inside the compiler? Should we discuss both or it requires another RFC?

At last, how much inconsistency would an implementation bring to the compiler if we must introduce them?

@djc
Copy link
Contributor Author

djc commented Feb 19, 2018

@WiSaGaN thanks for the feedback. I don't know much about the compiler internals, but I would guess the aliasing of unit and tuple types would not be very tied up in the enum variant handling, so I think it would make more sense to handle that in a different RFC. It would make sense to me, though, for much of the same reasons that I wrote up in this RFC.

@petrochenkov
Copy link
Contributor

Tracking issue for this feature - rust-lang/rust#26264.
It was previously considered not requiring an RFC, just implementation.

@petrochenkov
Copy link
Contributor

petrochenkov commented Feb 21, 2018

The previous (very much reasonable) decision in rust-lang/rust#26264 was to resolve variants exactly like inherent associated items - variants themselves as associated types and their constructors as associated values.
So it would be useful to compare passing generic arguments to both of them.

Ty<Args1>::AssocItem<Args2> or Trait<Args1>::AssocItem<Args2> means that Args1 are passed to the parent item and Args2 are passed to the associated item.
No argument transfer between segments happen, arguments are always passed to their proper segment.

For variants that would mean EnumAlias<Args1>::Variant and Enum<Args1>::Variant being the correct way pass generic arguments (variants don't have their own generic parameters (yet) so nothing is passed to them).

Enum::Variant<Args1> being legal and meaning Enum<Args1>::Variant exists for historical reasons and is, IMO, a wart. We should not extend it on type aliases (but we should keep it for backward compatibility of course). More details in https://internals.rust-lang.org/t/pre-rfc-enum-variants-through-type-aliases/6433/5.
So, I think we should go the EnumAlias<Args1>::Variant way instead of the scheme proposed in the RFC.

@djc
Copy link
Contributor Author

djc commented Feb 22, 2018

I agree with @petrochenkov that Enum<Arg>::Variant makes more sense in principle than Enum::Variant<Arg>. However, I don't think the interactions with the proposed change in this RFC are such that it makes sense to start the work towards that probably wider change in this RFC. There are two possible scenarios here:

  1. Aliased enum variants require arguments on type, not variant, but normal enum variants still require arguments on variant, not type. This drives towards the new desired syntax for aliased enums only, but introduces inconsistency between aliased and non-aliased enums, which somewhat defeats the purpose of this RFC to make aliases more similar to the original enums.
  2. Both aliased and non-aliased enum variants start preferring arguments on types, not variants; for aliased enums, they are required (since they're new anyway); for non-aliased enums variant arguments are deprecated (and presumably removed in the next epoch).

In the end, I think the change to move arguments from the variant to the type warrants its own discussion, since it a lot of code already out there, whereas this RFC restricts itself to slightly extending the allowed syntax to make aliases more useful. To maintain consistency (which I think are important to the ergonomics of aliases in general) the same rules should IMO apply to aliased and non-aliased enums.

@petrochenkov
Copy link
Contributor

petrochenkov commented Feb 23, 2018

cc #2218 ("Generics: Make enum variant syntax consistent with other types")

@nox
Copy link
Contributor

nox commented Feb 25, 2018

This would help Stylo a lot, which has a ton of type aliases to enums, and we need to import both the type alias and the enum it is based on if we want to build values of that type.

@aturon aturon self-assigned this Feb 28, 2018
@aturon
Copy link
Member

aturon commented Feb 28, 2018

Nominated for discussion in a lang team meeting.

@withoutboats
Copy link
Contributor

Removing I-nominated. I recall that we were generally in support of this feature, I think @nikomatsakis or @cramertj had more comments.

@djc
Copy link
Contributor Author

djc commented Mar 5, 2018

The meeting agenda/notes mentioned this:

  • Type alias enum variants
    • Allows to refer to enum variants through a type alias
    • Move to FCP to accept

@aturon
Copy link
Member

aturon commented Mar 9, 2018

We discussed this RFC in the lang team meeting, and would like to move it into formal review. It has addressed the open questions that prevented us from previously accepting a PR on this topic.

@rfcbot fcp merge

@rfcbot rfcbot added the proposed-final-comment-period Currently awaiting signoff of all team members in order to enter the final comment period. label Mar 9, 2018
@rfcbot
Copy link
Collaborator

rfcbot commented Mar 9, 2018

Team member @aturon has proposed to merge this. The next step is review by the rest of the tagged teams:

No concerns currently listed.

Once a majority of reviewers approve (and none object), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

@petrochenkov
Copy link
Contributor

@rfcbot concern #2338 (comment)

@djc
Copy link
Contributor Author

djc commented Mar 26, 2018

@nikomatsakis since a majority of the reviewers has approved now, can this enter FCP?

@rfcbot
Copy link
Collaborator

rfcbot commented Mar 26, 2018

🔔 This is now entering its final comment period, as per the review above. 🔔

@rfcbot rfcbot added final-comment-period Will be merged/postponed/closed in ~10 calendar days unless new substational objections are raised. and removed proposed-final-comment-period Currently awaiting signoff of all team members in order to enter the final comment period. labels Mar 26, 2018
@warlord500
Copy link

warlord500 commented Mar 29, 2018

if this rfc, were to be added to the compiler the following piece of code should compile correct?

enum Bar<T> {
  Test(i32),
  Data(T),
}

type Foo<T> = Bar<T>;
fn main() {
 let _may = Foo::Test::<i32>(1);
}

I do think it is weird were we put the type annotation. i feel like it should be closer to the type like

Vec::<i32>::new()

It seems inconsistent to me. I am safe in assuming that there will be no more additional enum type annotation syntax?

@djc
Copy link
Contributor Author

djc commented Mar 31, 2018

@warlord500 as explained quite clearly in the reference-level explanation, Foo::Test::<i32> is indeed the syntax made legal by this RFC. Note that, analogously, Bar::Test::<i32> is already how this is spelled today, so the changes from this RFC are orthogonal to the issue of how type arguments are passed to enum variants.

@warlord500
Copy link

warlord500 commented Apr 1, 2018

I am gonna apologize here! some of this comment, may come off as arrogant! also for possible any additional confusion this might add. Writing is not my strongest skill.
I have no intention of belittling anybody, let alone @djc. my assumptions on the last question was yes or no answers. which clearly was not received as intended.

I am gonna start off with, just because you find something clear doesn't
mean somebody else, finds the it clear.

In fact the very reason, I asked my question was to make sure I completely understood the RFC.
I believe me and you @djc have different definitions of syntax!
for example I define for something to be syntactically correct. it has to follow the correct grammar of the language. so for English it is subject verb object!
for example the sentence "the swing is below the water and above the water!" is grammatically correct.
but doesn't make any sense. unless we expand or change the meaning of the words above or below.
"run,skip,jump" are simply sentence fragments not useful, nor helpful on their own!

As, I understand the RFC as now, this doesn't change grammar but does expand the possible interpretations of foo::Test::(1); access foo which can be module or enum or struct,
access Test function or variant, fill generic argument 1 with i32, and call it with the value 1.
expands the access foo part so that to foo can be a type alias to enum, if the access is type alias so replace with it with the aliased type.

if my understanding of rust the language is wrong please correct me. It is never a bad time to learn.
In addition, as I understood your comment, the answer to my first question is yes,.
while the answer to my second answer is no!
if the second answer is yes, then the RFC needs to be changed to be more clear on how it changes the syntax!

If the first question's answer is no, then this RFC needs to be made clear in its entirety.
If the opposite, then I fully support the RFC. while I was testing this, I actually thought my last question would compile as is current rust. my only hope is that the RFC could be made clearer.
their are usage of words, I believe are used different than widely used definitions!

thank you, for reading my extremely long rant!

@djc
Copy link
Contributor Author

djc commented Apr 1, 2018

This RFC is indeed not intended to change the syntax. It just extends the semantics of the language so that referring to variants into aliased enums is now legal. I hope that clarifies things for you.

@warlord500
Copy link

thank you for short and sweet answer to my questions. @djc

@rfcbot
Copy link
Collaborator

rfcbot commented Apr 5, 2018

The final comment period is now complete.

@Centril Centril merged commit 1c851e0 into rust-lang:master Apr 5, 2018
@Centril
Copy link
Contributor

Centril commented Apr 5, 2018

Huzzah! This RFC has been merged!

Tracking issue: rust-lang/rust#49683

@killercup killercup mentioned this pull request May 2, 2018
11 tasks
@Centril Centril added A-paths Path related proposals & ideas A-typesystem Type system related proposals & ideas A-resolve Proposals relating to name resolution. labels Nov 23, 2018
type Alias<T> = Option<T>;

mod foo {
pub use Alias::Some;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm inclined to think this shouldn't be allowed, or at least it shouldn't be part of the initial implementation. After all, even

pub use Option::Some;

does not work right now, although

pub use ::std::option::Option::Some;

does, however.
For me, the semantics are unclear, and I'd be inclined not to accept it by default.

Anyway, would appreciate the thoughts of @rust-lang/lang on this matter.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this can't work right now.
Option::Some is module-relative (enums and traits are kind of modules from name resolution's point of view), so they can be used in imports.
Alias::Some is type-relative, and we need type checking to determine what Alias actually refers to, so it cannot be used in imports due to phase separation.

bors added a commit to rust-lang/rust that referenced this pull request Dec 11, 2018
Implement RFC 2338, "Type alias enum variants"

This PR implements [RFC 2338](rust-lang/rfcs#2338), allowing one to write code like the following.

```rust
#![feature(type_alias_enum_variants)]

enum Foo {
    Bar(i32),
    Baz { i: i32 },
}

type Alias = Foo;

fn main() {
    let t = Alias::Bar(0);
    let t = Alias::Baz { i: 0 };
    match t {
        Alias::Bar(_i) => {}
        Alias::Baz { i: _i } => {}
    }
}
```

Since `Self` can be considered a type alias in this context, it also enables using `Self::Variant` as both a constructor and pattern.

Fixes issues #56199 and #56611.

N.B., after discussing the syntax for type arguments on enum variants with @petrochenkov and @eddyb (there are also a few comments on the [tracking issue](#49683)), the consensus seems to be treat the syntax as follows, which ought to be backwards-compatible.

```rust
Option::<u8>::None; // OK
Option::None::<u8>; // OK, but lint in near future (hard error next edition?)
Alias::<u8>::None; // OK
Alias::None::<u8>; // Error
```

I do not know if this will need an FCP, but let's start one if so.

r? @petrochenkov
bors added a commit to rust-lang/rust that referenced this pull request Dec 29, 2018
Implement RFC 2338, "Type alias enum variants"

This PR implements [RFC 2338](rust-lang/rfcs#2338), allowing one to write code like the following.

```rust
#![feature(type_alias_enum_variants)]

enum Foo {
    Bar(i32),
    Baz { i: i32 },
}

type Alias = Foo;

fn main() {
    let t = Alias::Bar(0);
    let t = Alias::Baz { i: 0 };
    match t {
        Alias::Bar(_i) => {}
        Alias::Baz { i: _i } => {}
    }
}
```

Since `Self` can be considered a type alias in this context, it also enables using `Self::Variant` as both a constructor and pattern.

Fixes issues #56199 and #56611.

N.B., after discussing the syntax for type arguments on enum variants with @petrochenkov and @eddyb (there are also a few comments on the [tracking issue](#49683)), the consensus seems to be treat the syntax as follows, which ought to be backwards-compatible.

```rust
Option::<u8>::None; // OK
Option::None::<u8>; // OK, but lint in near future (hard error next edition?)
Alias::<u8>::None; // OK
Alias::None::<u8>; // Error
```

I do not know if this will need an FCP, but let's start one if so.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-paths Path related proposals & ideas A-resolve Proposals relating to name resolution. A-typesystem Type system related proposals & ideas final-comment-period Will be merged/postponed/closed in ~10 calendar days unless new substational objections are raised. T-lang Relevant to the language team, which will review and decide on the RFC.
Projects
None yet
Development

Successfully merging this pull request may close these issues.