My understanding is internally rustc represents structs as single-variant enums, so the sum-of-product-types is somewhat natural, but the opposite seems like it would need to define an anonymous type for the inner enum.
For fully anonymous enums I expect MyStruct is the namespace, so that would be disallowed. It could also be punted on, simply requiring the enums to have names. As long as we're discussing syntax, it might be nice for the enum keyword to still make an appearance, ala
I was just thinking about something like this after seeing this post regarding context in errors. Although I had the inverted idea of adding variant independent fields to enums.
enum MyEnum {
y: f64; // present for all variants
V1,
V2,
}
The idea for this was to add a context to enum style errors without needing to duplicate the field into every variant.
I know this isn't exactly a sum type in a product type. It's adding fields to enums rather than adding variants to structs, but maybe it can serve as an example use case.
I just don't know how useful these constructs would be in practice. I don't run into these situations often enough that I feel saving a few keys by not having to write a separate type is necessary.
Instead of saying "we can treat a sum type as a generalisation of an enumerated type", it says "we can treat a pair of a product type and a sum type as a generalisation of a record type".
C allows anonymous unions (and structs too) inside structs. Typescript has union types (spelled foo | bar | baz) that are very often used in ad hoc style inside types and in function parameters. Scala has similar union types as well. And there have been previous discussions about adding anonymous enums to Rust too – in particular in the context of error management.
Attributes could be added, but then we only support traits which have derive() macros available? That seems…unfortunate. This feels very adjacent to "enum variant types" (I'm unsure of the most recent status/issue) where enum MyEnum { A { x: u64 } } can be "captured" as A { x: u64 } after matching on an instance of MyEnum. Given that MyEnum::A is not a type, this doesn't work today. However, this does not affect #[derive()] for MyEnum as much because the default behaviors over a sum type are easy. With a product type, the questions have to be answered for all fields anyways (Default is the main problem here). With a nested sum type, the compiler now has to…make a choice. I suppose #[default] can work here, but I'm hesitant to say it is the only trait affected. But maybe it's fine and #[default] and other derive()-associated attributes are all that's needed. But it is unfortunate that one is limited ot derive() macros for any impl Trait for InnerEnum fields.
This means that StructName::VariantName1 can only be used in destructuring context (like in match or if let clause) where it is clear of which VariantName1 we are talking about, and it would not be possible to use it in function parameters, return type or explicit variable type.
Personally I would consider this an anti-feature for the simple reason that it doesn't solve anything, or make anything new possible, yet still wants to spend the really scarce complexity budget that the language has left to spend.
If this complexity budget threshold is exceeded, we end up with C++: a language so gargantuan that Cthulhu looks like a Chihuahua in comparison. A language that cannot reasonably be used fully within a project, or fully understood by 1 person, with the undesirable side effect that projects have to define allowed dialects of the language, splintering knowledge and the ecosystem in the process.
No single change will make that happen, but changes like this would surely push Rust closer to that edge, with nothing useful in return.
So, in principle, you are against product types within sum types?
Yet it is part of the language, so obviously the language goal and your principles do not match.
You need some way to make name the variant anyways, so it may as well be a separate type. No such requirement applies to product types.
There are downsides and upsides to both. Layout optimizations are an obvious upside, but people also get annoyed that you can't use enum variants as types, which forces them to split out the products into separate types anyways.
I've given a way to make the variants in my original post. As have some others as replies.
And the "people getting annoyed that you can't use enum variants as types" would also apply to the product types within sum types scenario, with people not being able to use the inner product type.