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 upFuture-proofing enums/structs with #[non_exhaustive] attribute #2008
Conversation
This comment has been minimized.
This comment has been minimized.
|
Another thing in favour of |
This comment has been minimized.
This comment has been minimized.
|
Because you mentioned it: if there's enough support for it, I'd be more than happy to promote the struct/trait parts into the main RFC. I've mostly kept what's there small so that we can at least get the enum part in. Part of the reason why I did that is also ensuring that we choose a name that would work for structs/traits if/when they get added too. |
aturon
assigned
nikomatsakis
May 25, 2017
aturon
added
the
T-lang
label
May 25, 2017
This comment has been minimized.
This comment has been minimized.
|
I like it This RFC cements the current state of the world with respect to exhaustive matching: where a producer's If I understand the RFC correctly, the |
This comment has been minimized.
This comment has been minimized.
|
I feel a bit uneasy about using an attribute to control this. I'd prefer |
This comment has been minimized.
This comment has been minimized.
dlight
commented
May 26, 2017
A tangential comment: |
This comment has been minimized.
This comment has been minimized.
I'd kind of expect to eventually have keywords |
kevincox
approved these changes
May 26, 2017
kevincox left a comment
|
Excellent writeup. This sounds like a great change to me. |
| Should there be a way to warn downstream crate users when their match is | ||
| non-exuhaustive? What if a user wants to add a warning to their matches using | ||
| non-exhaustive enums, to indicate that more code should be added to handle a new | ||
| variant? |
This comment has been minimized.
This comment has been minimized.
kevincox
May 26, 2017
I would argue that if you expect users to handle every case then this isn't for you. You should not use this attribute and bump your major version. A warning on not handling a particular case is also incredibly likely to be low-value noise.
This comment has been minimized.
This comment has been minimized.
clarfon
May 26, 2017
Author
Contributor
That's fair! I mostly carried this over from #757 because it was discussed a lot in the RFC there.
| ## Extensions to structs | ||
|
|
||
| It makes sense to eventually allow this attribute on structs as well. Unlike | ||
| enums, it's very easy to make a struct non-exhaustive: |
This comment has been minimized.
This comment has been minimized.
kevincox
May 26, 2017
This seems like a good idea to me. However in this case non-exhaustive seems a less relevant keyword word because it is usually used to describe matching. incomplete, extensible or will_extend now start to sound better and work for both.
This comment has been minimized.
This comment has been minimized.
clarfon
May 26, 2017
Author
Contributor
I'd also note here that the term "extensible" was used on the previous RFC.
|
|
||
| Tangentially, it also makes sense to have non-exhaustive traits as well, even | ||
| though they'd be non-exhaustive in a different way. Take this example from | ||
| [`byteorder`]: |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
whitequark
May 26, 2017
•
Member
I would say #[extensible] applies here just as well--as in extending the set of supertraits. In case we go for .. for enum and struct (which I think is a good idea), we could go with:
trait A: B + C + .. {}
This comment has been minimized.
This comment has been minimized.
kevincox
May 26, 2017
You are right. extensible does sound good. Not only for extending traits but also extending the provided methods. And when I do look at it that way it does seem fairly related.
This comment has been minimized.
This comment has been minimized.
clarfon
May 26, 2017
Author
Contributor
You're right that they seem sufficiently different, but I see them all as solving the same problem: instead of preventing a user from implementing/using something by creating a hidden field, this simply states that not all of the dependencies can be listed by the user.
This comment has been minimized.
This comment has been minimized.
That is fair! I mostly changed it to an attribute because at the time of the original RFC (#757), it was decided that adding extra syntax was not worth the benefits, and someone suggested that an attribute might be better. That said, a lot has happened since the initial 1.0 release, and I think that people are more willing to accept a syntax addition for this now than when the original RFC was written. |
This comment has been minimized.
This comment has been minimized.
|
|
This comment has been minimized.
This comment has been minimized.
|
If this RFC is accepted and stabilized, there should be a (clippy?) lint that suggests any C-like enum returned from extern function ( |
clarfon
changed the title
Future-proofing enums with #[non_exhaustive] attribute
Future-proofing enums/structs with #[non_exhaustive] attribute
May 26, 2017
This comment has been minimized.
This comment has been minimized.
leodasvacas
commented
May 28, 2017
•
@scottmcm I don't see how the attribute would allow FRU, to the contrary it should forbid FRU outside the module that defines the struct. This should be in the RFC. |
This comment has been minimized.
This comment has been minimized.
|
@kennytm that doesn't really make much sense. A match on a non-exhaustive enum outside the bounds would still be UB, and it's not uncommon for C enums to be treated like Rust enums - i.e., only within the bounds is okay. |
This comment has been minimized.
This comment has been minimized.
|
@ubsan a match on a non-exhaustive C-like enum, not arbitrary enum. This particular case should be definable. |
This comment has been minimized.
This comment has been minimized.
|
I'm pretty sure that casting an arbitrary integer to an enum, C-like or not, is undefined behaviour unless it corresponds to a valid variant. I don't think that annotating the enum differently is going to change this problem. |
This comment has been minimized.
This comment has been minimized.
|
@leodasvacas Oops, you're absolutely correct. Allowing FRU would need it to use the alternative desugar. ("jFransham's Super-FRUs", I remember it being called...) |
This comment has been minimized.
This comment has been minimized.
le-jzr
commented
May 29, 2017
|
@clarcharr What makes it UB for C-like enums is that it fails the assumption that an exhaustive match will always execute one of its branches. If exhaustive match is impossible, missing labels pose no problem. |
This comment has been minimized.
This comment has been minimized.
le-jzr
commented
May 29, 2017
|
Or to put it simply, putting #[non_exhaustive] on C-like enum could be a statement that every value of the underlying integer type is valid, even if those values don't have labels. Insofar as that statement applies to all code, even within the same crate, which of course is not what's being proposed in this RFC. |
This comment has been minimized.
This comment has been minimized.
|
Note that the attribute does not say "all bit patterns of this enum are valid," only "more variants may be added in the future." I don't think that this is a good solution to the problem you're suggesting. |
This comment has been minimized.
This comment has been minimized.
le-jzr
commented
May 29, 2017
•
|
The attribute says what the RFC says it says, and RFC is not merged yet. ;) But yes, it's somewhat different, even if intimately related. The solution to C enum problem requires |
This comment has been minimized.
This comment has been minimized.
|
I do want some "real" solution for this. I want to be able to machine-check semver comparability, and the "doc trick" will be ugly to implement for any tool which does that. |
This comment has been minimized.
This comment has been minimized.
Virtlink
commented
May 31, 2017
|
This solution would be welcome, as it's a non-breaking change for Rust 1.x. But I think it's the wrong way around. Non-exhaustive should be the default. This allows a crate author to add new enum variants and add new public and private fields to a struct, without breaking user's code. Of course, this forces users to handle the case where enums are extended, prevents users from using the default constructor to construct a struct, and also prevent users from destructuring the struct. If the author really wants to enable the user to construct and destruct structs and exhaustively match enums, they have to promise that they won't add members to the struct or enum by finalizing it through some keyword or attribute (or otherwise risk breaking user's code). Maybe for Rust 2.0? |
bjorn3
reviewed
May 31, 2017
| This pattern could again be solved by using `#[non_exhaustive]`: | ||
|
|
||
| ```rust | ||
| #[non_exhaustive] |
This comment has been minimized.
This comment has been minimized.
bjorn3
May 31, 2017
I thinkno_extern_impl is a better name, because exhaustivenes feels weird for traits.
This comment has been minimized.
This comment has been minimized.
|
@clarcharr Are there any updates from the FCP you plan to make to this? |
This comment has been minimized.
This comment has been minimized.
|
Sorry for the delays @petrochenkov and @scottmcm. I will try and update this tomorrow. |
alrz
referenced this pull request
Aug 24, 2017
Open
Proposal: sealed enums (completeness checking in switch statements) #782
This comment has been minimized.
This comment has been minimized.
|
@clarcharr Ping on the updates? |
This comment has been minimized.
This comment has been minimized.
|
@aturon @scottmcm @petrochenkov Sorry for the delays; I've updated with the notes on FRU and the tuple struct constructor visibility. Is this good to merge? |
petrochenkov
reviewed
Aug 26, 2017
| ## Functional record updates | ||
|
|
||
| Functional record updates will operate exactly the same regardless of whether | ||
| structs are marked as non-exhaustive or not. For example, given this struct: |
This comment has been minimized.
This comment has been minimized.
petrochenkov
Aug 26, 2017
•
Contributor
Hm, the decision was the opposite - FRU is not permitted with non_exhaustive structs (in other crates).
FRU (with current rules) is a promise to not add private fields to a struct, and we want to be able to add private fields to non_exhaustive structs.
This comment has been minimized.
This comment has been minimized.
clarfon
Aug 26, 2017
Author
Contributor
I thought that FRU still worked when structs have private fields?
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
clarfon
Aug 26, 2017
Author
Contributor
Unless I'm misunderstanding, I tried this out on the playground and it seems to work: https://play.rust-lang.org/?gist=eabe36303f9da0ced44c237ca3b29d45&version=stable
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
petrochenkov
reviewed
Aug 26, 2017
| @@ -417,6 +417,10 @@ Then we the only valid way of matching will be: | |||
| let Config { 0: width, 1: height, .. } = config; | |||
| ``` | |||
|
|
|||
| We can think of this as lowering the visibility of the constructor to | |||
| `pub(crate)` if it is marked as `pub`, then applying the standard structure | |||
| rules. | |||
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
petrochenkov
reviewed
Aug 26, 2017
|
|
||
| ``` | ||
| #[non_exhaustive] | ||
| pub struct Unit {} |
This comment has been minimized.
This comment has been minimized.
petrochenkov
Aug 26, 2017
Contributor
Well, this seems to be equivalent to 3d56889#r135395315, but I'd still prefer to use the same wording for tuple and unit structs.
clarfon
added some commits
Aug 26, 2017
This comment has been minimized.
This comment has been minimized.
|
LGTM now |
scottmcm
referenced this pull request
Aug 26, 2017
Open
Tracking issue for RFC 2008: Future-proofing enums/structs with #[non_exhaustive] attribute #44109
This comment has been minimized.
This comment has been minimized.
|
Huzzah! The @rust-lang/lang team has decided to accept this RFC. To track further discussion, subscribe to the tracking issue here: rust-lang/rust#44109 Thanks for the RFC, @clarcharr! |
clarfon commentedMay 25, 2017
•
edited by scottmcm
This is a post-1.0 version of #757, changed to use an attribute instead of a dedicated syntax. This would allow crates to more easily mark enums as "non-exhaustive," requiring downstream crates to add a wildcard arm to matches, so that adding new variants is not a breaking change.
Additionally, this syntax is extended to structs as well.
Rendered
Edit: Updated rendered link to rfcs repo version.