Pre-RFC: Enum variants through type aliases

Let’s start from the beginning.
Suppose we have a generic enum

enum E<T> {
    V,
    /* other variants */
}

// Make all variants available in "naked" form
use E::*;

and we want to create an example of variant V.

let v1 = E::V;
let v2 = V;

We get something like ERROR: cannot infer generic parameter `T` in both cases.

Indeed, we need to provide a generic argument for T, which is a generic parameter on enum, not variant. Variants themselves don’t have generic parameters.

For the “qualified” variant path it seems obvious we should write

let v1 = E::<u8>::V; // OK, we supplied arguments for the *enum*

but what should we do for the “naked” variant let v2 = V?
The solution is kinda hacky, but let’s specify enum’s arguments right on the variant, even if they don’t belong to it, because it’s the only possible place - there’s nothing except for V in the path.

let v2 = V::<u8>; // OK, but kinda hacky

So, what is the problem? Why E::<u8>::V has to be written in the hacky way as well (E::V::<u8>)?
The answer: “naked” variants entered the language first, “qualified” enums were added only few months before 1.0 and released in 1.0.0-alpha. There was strong focus on quickly achieving stability back then, so qualified enums inherited the way of supplying generic arguments from naked enums, and it stuck.


Now, let’s see how aliases change the picture.

  • First, we still need to provide arguments for enum, and only for enum.
  • Second, we can’t merge two lists of arguments (E::<A>::V::<B>), that doesn’t make much sense, generic arguments are positional, not named. (Okay, we can take both lists and unify them with each other accepting E::<u8>::V::<u8> and rejecting E::<u8>::V::<u16>, but why on earth would we do that if there are simpler options.)
  • Third, Alias in Alias::V can already contain parameter substitutions in it implicitly (those specified in type).
    type Alias = E<A>;
    let v = Alias::V<B>;
    
    so by accepting Alias::V<B> we ought to merge two lists of arguments, but we can’t merge two lists of arguments.

The only possible remaining design follows from these listed constraints:

// Suppose `Anything` can be both an enum or an alias, `Alias` can only be an alias, `NonAlias` can only be an enum.

// Canonical forms
let v = Anything::<A>::Variant; // OK, the canonical qualified form. Enum's generic arguments are actually supplied to the enum.
let v = Variant::<A>; // OK, the canonical naked form, out of necessity, `Variant` is the single available place to put parameters.

// Legacy forms
let v = NonAlias::V::<A>; // Legacy OK, can't break code.

// Everything else is an error
let v = Anything::<A>::V::<B>; // ERROR, can't merge substs
let v = Alias::V::<A>; // ERROR, can't merge substs
1 Like