Considering sum type nature of sum-enums, they have the following properties: [âŠ]
enum(A, B, A)is equivalent to enum(A, B)
That is then definitely not a sum type (with all their implementation and conceptual problems). I think calling it as such is even more confusing than a âunion typeâ (because those who would confuse them probably donât even know about unions in Rust, which are most useful for FFI purposes). Iâd prefer something like ânormalized variantâ or ânormalized alternativeâ types for avoiding confusion with unions and real sum types (enums).
into is required for conversion into implicitly constructed sum-enum
[âŠ]
The main way to create sum-enums will be to use Into trait implementation.
If we are adding a core language feature, Iâd opt for making this a coercion instead, guided by type inference. This would have two advantages:
- It seems to me that you want to make the enum-like nature of these types unimportant, or treat them like so (because you mentioned that
enum(A) == A, unlike a single-variant named enum). This means that it is semantically redundant to have to use .into() because the fact that thereâs a discriminant and enum-like behavior is basically an implementation detail at this point (especially when itâs in a "return impl Trait context). Just like you (hopefully) donât write fn foo<T>(x: T) -> T { x.into() }, and just return x directly.
- Automatically implementing
Into would presumably require compiler support, either via making Into or From lang items, or otherwise baking them into the language. The beauty of these two conversion traits is that they donât require this. This is a pretty big conceptual change and a point of no return, so Iâd be wary of committing to it.
In cases when ordering of type arguments is required it can be done based on TypeId of each type.
Alternatively for tag we could use TypeId directly
Doesnât TypeId have a non-uniqueness bug? I mean, I get that itâs a hash, but last time I read about it, IIRC some of the compiler/runtime developers warned that it has a way higher than negligible/acceptable chance of collision for realistically complex types. If this is the case, using it as a discriminant or an ordering key can cause unsoundness and memory unsafety bugs, because it would result in accidentally interpreting values as the wrong type.
Others have already mentioned the problematic parts of the interaction with generics (which I mostly agree with), so I wonât reiterate them here.
Unresolved questions
Interaction with lifetimes. To start things off we could restrict sum-enum usage only with types which ascribe to 'static
I might be naive, but isnât the lifetime of a union of types simply the intersection of the lifetimes of its components?
impl Trait variations: some have proposed to use enum impl Trait
IMO enum impl Trait is just noise (actually, worse, the leaking of an implementation detail).
Syntax bikeshed: enum(T, U) is meh at best, because that looks like a tuple. I think Centril proposed an alternative syntax in the other thread T | U which is nicer to read, as it is evocative of semantics beacuse it denotes the âorâ logical operation (corresponding to set union). It also gets rid of a pair of parenthesis and the keyword, but thatâs a minor point.