-
Notifications
You must be signed in to change notification settings - Fork 707
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
Emit #[non_exhaustive] for Rustified enums? #1554
Comments
I agree, this would be a nice feature, and would be relatively straight-forward to implement. I'd be happy to mentor it. |
Hi! If you have any questions regarding this issue, feel free to make a comment here, or ask it in the If you intend to work on this issue, then add |
I'm not sure what the interface for this should look like. So far I came up with two ideas:
|
Something to potentially consider is whether it's likely people want some but not all enums to be non-exhaustive. But the initial version could just probably be an all-or-nothing switch. (2) sounds nicer to me. |
Would it perhaps be better to flip the assumption and emit Obviously |
Well, perhaps, but that'd be a breaking change, and note that rustified enum is already opt-in. |
Just made a PR for this, let me know if anything needs to be changed. It looks like stabilization of non-exhaustive enums is gonna land soon, I guess the PR will stay un limbo until then. |
Implements the feature discussed in #1554.
Note that the |
|
I think that the documentation for rustified_enum is misleading: non_exaustive does not solve the issue rust-lang/rust#36927. Using an enum with a bit pattern with no corresponding variant is still UB. See comments 1 and 2, also in the nomicon. I believe that the best way would be to return an option while converting the primitve to the enum type. Like the FromPrimitive trait, which can be derived using num-derive. Another option would be adding an Unknown variant, which would also need to be created manually when converting from the integer value. |
Option and MyEnum::Unknown don't solve the fundamental problem that C can send a bit pattern to you that's not allowed for the enum and you're in UB land. Using the "rustified enums" bindgen style is just extremely dangerous all the way through. I would 100% always tell people to avoid it. |
@Lokathor If receiving directly the enum value, a struct with it or transmuting integers, I agree, but not if receiving the primitive and calling an function which matches the primitive and returns Unknown in the wildcard branch. Creating the value in rust and passing it to C is also fine. #[derive(FromPrimitiveUnknown)]
enum Example {
A = 0,
B,
C,
#[from_primitive::unknown]
Unknown = 0xFF,
}
// This would be auto generated by derive macro...
// Possibly this macro could instead implement From<u32> for Example
impl FromPrimitiveUnkown for Example {
fn from_u32(n: u32) -> Example {
use Example::*;
match n {
0 => A,
1 => B,
2 => C,
_ => Unknown,
}
}
}
fn main() {
let my_value = 1; // Received from C or network
let my_value = Example::from_u32(my_value); // B
let my_other_value = 20; // Received from C
let my_other_value = Example::from_u32(my_other_value); // Unknown
match my_value {
... // Exhaustive match!
}
} For example, I've got an use-case where the enum value is actually contained in an u8 inside a struct (I find this to be quite common, as C enums are always 32-bits...) and, although I'm converting from u8 to u32 and then matching the constified enum right now, deriving FromPrimitive or the imaginary FromPrimitiveUnkown would both be a lot nicer, since I'd then be able to match exhaustively on the enum (instead of non_exhaustively on the u32) and the compiler would catch any time I've missed a recently added value. Simply deriving FromPrimitive would be enough, which could probably be supported if #1089 is implemented. Going back on the documentation topic, maybe the docs for both |
Yes, I agree with making it integers at the actual FFI bound and then converting into a rustified form with TryFrom or similar. That's what I've always done myself. |
There is also the newtype enum style I added a few months ago which could be used across an FFI safely. The only catch would be you lose the enum Debug derive. |
@auscompgeek that's really nice!! I didn't know you could match on newtypes / transparent newtypes... Can we please update the docs pointing to it though? I believe that non_exhaustive should ideally be I feel like the docs pointing to non_exhaustive are a real problem as people will likely believe that the actual solution is using In my opinion, the docs for both |
non_exhaustive won't be deprecated in the language in general. It isn't meant for ffi at all, it's for API stability purposes. If you meant "depricated in bindgen", it should be removed entirely really. It's essentially never ever correct to use in bindings. |
Yes, I mean in bindgen, in rust its actually great for its intended purpose. I thought to deprecate it as it wouldn't be a breaking change, hadn't realized that bindgen is still in 0.X... Also agree that removing is the best idea. 👍 Should I just open a PR? @emilio can you weigh in? |
(thinking a bit out-loud, so sorry for the back and forth) I can think of potential use cases for it, like binding against a library you control (so you know that there won't be out-of-range enum variants), but wanting the same forward-compat guarantees than But at that point you are equally served by the non-rustified enums... With one gotcha, which is the following: Does non_exhaustive allow to exhaustively match inside your crate? I guess it should. In that case, I think there's still one potential good use for In any case I agree it shouldn't be the recommended way, and that it should be properly explained that this doesn't prevent UB for out-of-range variants. I think for now I'd go with a note / warning / explanation on the docs (specially since the feature is not terribly hard to maintain). |
non_exhaustive still allows for an exhaustive match within the crate that declared the type, yes. |
Yeah, my usecase is a third-party library that will likely add new variants to their enums in future. However now that we have the newtype enum style that will probably fit my usecase better. Given we already have non_exhaustive implemented and it's a simple feature which others may find useful, I don't see a reason to remove it. I do agree better documentation warning about the implications would be best though (and maybe a pointer to the newtype style which might fit people's usecase better). |
Now that the docs were fixed, can this issue be closed? |
Yes, let's close this, thanks all! |
It'd be good to have an option to emit #[non_exhaustive] on Rustified enums.
The text was updated successfully, but these errors were encountered: