Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upAllow arbitrary enums to have explicit discriminants #2363
Conversation
Centril
added
the
T-lang
label
Mar 16, 2018
oli-obk
reviewed
Mar 16, 2018
| This introduces one more knob to the representation of enumerations. | ||
|
|
||
| # Rationale and alternatives | ||
| [alternatives]: #alternatives |
This comment has been minimized.
This comment has been minimized.
oli-obk
Mar 16, 2018
Contributor
I think you have encountered the XY Problem. You are finding a solution to the problem that different enums don't give guarantees about their variant indices, when what you originally wanted was converting enums whose variants have fields into enums whose variants have no fields.
So here's my alternative:
Allow annotating enums with #[discriminant(OtherEnum)], where OtherEnum must have a variant for each variant in the annotated enum (with matching name). The annotated enum will then take all its discriminant values from OtherEnum.
as casts could then convert from annotated enums to their discriminant enum.
This comment has been minimized.
This comment has been minimized.
eddyb
Mar 16, 2018
Member
That seems more convoluted, IMO. Definitely harder to implement, at least.
This comment has been minimized.
This comment has been minimized.
oli-obk
Mar 16, 2018
Contributor
Sure, but it has a path forward for having this operation in safe code.
Alternatively a procedural macro could generate said conversions with this RFC. So the compiler magic might not be necessary.
This comment has been minimized.
This comment has been minimized.
nox
Mar 16, 2018
•
Author
Contributor
This would be great and nice, if only real world allowed me to do that.
I simplified my use case for the sake of the RFC: PropertyDeclaration actually has more variants than LonghandId, thanks to custom CSS properties (and some other stuff that I don't need to mention here):
#[repr(u16)]
enum PropertyDeclaration {
Color(Color),
Height(Length),
InlineSize(Length),
TransformOrigin(TransformOrigin),
Custom(CustomDeclaration),
}
struct CustomDeclaration {
name: Name,
value: Value,
}
pub enum PropertyDeclarationId<'a> {
Longhand(LonghandId),
Custom(&'a Name),
}
impl PropertyDeclaration {
pub fn id(&self) -> PropertyDeclarationId {
if let PropertyDeclaration::Custom(ref declaration) = *self {
return PropertyDeclarationId::Custom(&declaration.name);
}
let id = unsafe { *(self as *const _ as *const LonghandId) };
PropertyDeclarationId::Longhand(id)
}
}In general, I think this alternative is too specific to the exact use case described in the RFC, and cannot fulfil more intricate ones like the actual stuff I require in Servo, or use cases I could imagine for this feature with FFI, @Gankro may have an opinion on that regard here.
Edit: I'll edit this RFC to include ite nonetheless.
This comment has been minimized.
This comment has been minimized.
Diggsey
Mar 16, 2018
Contributor
You mention in the RFC that rust is generating a 4KB jump table for the required match expression - this seems excessive, even if the discriminants don't match exactly. How many variants does this enum have? Maybe part of the solution should be improving the code that rust generates in this case.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
burdges
commented
Mar 17, 2018
•
|
It'd be cool if this worked with ATCs to permit defining both discriminant and enum together:
We've a circular definition above, but rustc could presumably break this cycle. |
This comment has been minimized.
This comment has been minimized.
|
@burdges That seems unrelated to the RFC described in this pull request. |
This comment has been minimized.
This comment has been minimized.
|
I can totally sympathize with this RFC. I have often wanted to go from an enum with fields to a value of another type. My biggest two gripes against this RFC are
In that light I propose the following variant/alternative: // same as before
#[derive(Clone, Copy)]
#[repr(u16)]
enum LonghandId {
Color,
Height,
InlineSize,
TransformOrigin,
}
// explicitly mention that the given type is the descriminant (it can also be a struct!),
// but the compiler needs to be able to prove at compile time that two values of the
// descriminant type are actually different. Note: different means different bit patterns,
// not different in the `Eq` sense.
#[repr(LonghandId)]
enum AnimationValue {
Color(Color) = LonghandId::Color, // no cast
Height(Length) = LonghandId::Height, // also each descriminant assignment must be unique!
InlineSize(Void) = LonghandId::InlineSize,
TransformOrigin(TransformOrigin) = LonghandId::TransformOrigin,
}
impl AnimationValue {
fn id(&self) -> LonghandId {
(*self) as LonghandId // compiler can just return the descriminant! Completely safe!
}
} |
This comment has been minimized.
This comment has been minimized.
burdges
commented
Mar 20, 2018
|
We could support discriminants with their own fields too, making this these enums into extensions. I suppose |
This comment has been minimized.
This comment has been minimized.
|
Please, I have already addressed that this doesn't fit my use case in #2363 (comment) and the RFC already includes such an encoding as an alternative, except with Removing the need for unsafe code for the cast is a nice thought, but again it is unrelated to this RFC. |
This comment has been minimized.
This comment has been minimized.
|
@nox Sorry if this is a stupid question, but I still don't understand how the two proposals are not functionally equivalent. It seems like everything you can do in one proposal, you can do in the other. What am I missing? |
This comment has been minimized.
This comment has been minimized.
|
I assume this is restricted to enums with a non-Rust |
This comment has been minimized.
This comment has been minimized.
|
@RalfJung I had the same reaction. nox and eddyb explained that this RFC controls the discriminant of each variant, but this is a separate concept from the tag that may or may not be part of the memory representation. But yes, since |
This comment has been minimized.
This comment has been minimized.
|
@mark-i-m #2363 (comment) One of the enums with fields has more variants than |
This comment has been minimized.
This comment has been minimized.
|
@SimonSapin I am not sure I understand, but if the feature is not useful for |
This comment has been minimized.
This comment has been minimized.
|
@RalfJung I listed it in the unresolved questions. I could imagine rustc managing to use niche-filling optimisations for 2 different variants with an inner enum, if it could see that they use disjoint sets of discriminants and that there is an easy way to compute the discriminant from the niche value. This is quite theoretical though so I don't mind mandating a |
This comment has been minimized.
This comment has been minimized.
Evrey
commented
Mar 21, 2018
•
|
I realy like the With
Also, There are two problems left for my taste:
Now, about the first point... I especially like the #[repr(u8)]
enum X {
A(u32) = 0,
B(f32) = 1,
}
assert!(X::A(42).id() == X::A);
assert!(0_u8 == X::A);This could, however, mean trouble if So... how do we get that discriminant? Writing a pointer-converting boiler plate function called I'm not really up to date on the An alternative would be to treat enums logically as a Edit: Just noticed this:
Under the default representation, there might be heavy layout optimisations at work merging discriminants of nested enums into a completely different numeric set. In addition to that |
This comment has been minimized.
This comment has been minimized.
|
@Evrey I'll add a commit listing all your arguments against How does one name those explicit discriminants?We can't interpret How does one get those discriminants?I agree that a way to safely get the |
This comment has been minimized.
This comment has been minimized.
|
I see... but I still have the same two annoyances against the current form:
Especially the second one seems like an essential part of any reasonable solution IMHO... |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Given what I said about |
This comment has been minimized.
This comment has been minimized.
main--
commented
Mar 22, 2018
Note that this is #1872. Feels like a quite elegant workaround to me - the unsafe cast asserts that the |
This comment has been minimized.
This comment has been minimized.
|
@main-- I know about this RFC, but it has been postponed and I don't think I should have to rely on that instead of just not having |
This comment has been minimized.
This comment has been minimized.
main--
commented
Mar 22, 2018
|
@nox I mean, you're essentially trying to come up with a way to define Rust enums with gaps in their discriminator space - this RFC proposes to explicitly specify the remaining ones whereas I suggested to list the missing ones. It's fundamentally the same thing, depending on the usecase either one may be more convenient. Yet I favor the |
This comment has been minimized.
This comment has been minimized.
|
Having to specifically define variants that are uninhabited for no other reason than to make 2 discriminants in 2 different enums coincide is a damn workaround that I will not qualify as "fundamentally the same thing" as being able to omit said variants. Why would I pollute my mind and my type definition with variants that I won't use? |
This comment has been minimized.
This comment has been minimized.
main--
commented
Mar 22, 2018
|
I guess if most of your variants are dead it makes considerably less sense than, say, one of thirty. The thing is - comparing a language feature tailored specifically to this problem to a general mechanism that the language already offers (minus a papercut) just isn't fair, hence my reluctance to call it a workaround. My point is that the alternative is not simply omitting the dead variants. Instead, you have to manually specify discriminants. When I say it's the same thing what I mean is that we're talking about two different ways to specify enum discriminants (same outcome). Your proposal introduces a new language feature which is clearly an improvement over the (mostly) existing workaround, I merely doubt that it's enough of an improvement to justify the complexity. |
This comment has been minimized.
This comment has been minimized.
|
This is a very small addition to the language, semantics-wise and implementation-wise. As far as I remember discussions with @eddyb, this will be easily implementable. |
This comment has been minimized.
This comment has been minimized.
|
I don’t believe added complexity is significant here, the same syntax already exists on field-less variants. |
This comment has been minimized.
This comment has been minimized.
|
I went back and re-read the RFC and I think my objections are more specific about the example. The actual proposal is just about allowing |
This comment has been minimized.
This comment has been minimized.
|
(Aside: there’s a bunch of things in Stylo that are objectionable ;) (I say this as the author of many of these things.) A lot of it can be improved but the devil is in the corner cases, and better solutions are often not as easy as they first seem.) |
This comment has been minimized.
This comment has been minimized.
|
I may be not reading correctly, but does this work for
have size zero or one? Also, would:
have size 0 or 2? (These are super esoteric examples but it seems like a valid thing to ask considering how imho these don't make sense, but would be accepted.) |
This comment has been minimized.
This comment has been minimized.
|
I wouldn’t say that they don’t make sense, merely that currently (as long as |
This comment has been minimized.
This comment has been minimized.
|
Could you create a complete example of correct usage of this feature so that people can be directed to it? Possibly toss it in the guide-level section? (re: the actual proposal, seems good to me! I don't think any part of firefox I work on has a use-case for it at the moment, though) |
This comment has been minimized.
This comment has been minimized.
|
@clarcharr I would expect both of them to have size |
cramertj
self-assigned this
Apr 19, 2018
This comment has been minimized.
This comment has been minimized.
|
Copied from rust-lang/rust#46213, where I posted it by mistake: I just now realized that In fact, I believe that the only way you can observe the discriminant of a variant of a |
This comment has been minimized.
This comment has been minimized.
|
I think There's an important distinction to make, though:
(see also rust-lang/rust#49938) |
Centril
added
A-enum
A-discriminant
A-repr
A-syntax
labels
Nov 22, 2018
This comment has been minimized.
This comment has been minimized.
|
What is blocking this? |
nox commentedMar 16, 2018
•
edited
Summary
This RFC gives users a way to control the discriminants of variants of all enumerations, not just the ones that are shaped like C-like enums (i.e. where all the variants have no fields).
Thanks
Thanks to Mazdak Farrokhzad (@Centril) and Simon Sapin (@SimonSapin) for the reviews, and my local bakery for their delicious baguettes.🥖 🥖
Rendered