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 upRestrict constants in patterns #1445
Conversation
nikomatsakis
added
the
T-lang
label
Jan 6, 2016
nagisa
reviewed
Jan 6, 2016
| | u8 | u16 | u32 | u64 | usize // unsigned integers | ||
| | char // characters | ||
| | bool // booleans | ||
| | (B, ..., B) // tuples of builtin types |
This comment has been minimized.
This comment has been minimized.
nagisa
Jan 6, 2016
Contributor
Might be useful to consider including &str and arrays as well, since they have a well defined “primitive” memory-representation equality.
nagisa
reviewed
Jan 6, 2016
|
|
||
| Here, although it may well be that `T::X == T::Y`, we can't know for | ||
| sure. So, for consistency, we may wish to treat all constants opaquely | ||
| regardless of whether we are in a generic context or not. |
This comment has been minimized.
This comment has been minimized.
nagisa
Jan 6, 2016
Contributor
This is a pretty good point. If we opt to do the checking for regular constants, the contents of constant becomes a public API – changing these becomes a breaking change, which is pretty much counters almost whole consts’ purpose.
This comment has been minimized.
This comment has been minimized.
oli-obk
Jan 11, 2016
Contributor
Alternatively, in the future, we could forbid this kind of matching if it cannot be guaranteed that T::X != T::Y (which could be generically guaranteed by allowing such a statement in where clauses)
This comment has been minimized.
This comment has been minimized.
|
Pretty strongly in favour. |
This comment has been minimized.
This comment has been minimized.
|
This RFC is a good idea. Since it only affects matching on constants of non-primitive types which is quite the sticky situation, I am in favor of this RFC. |
petrochenkov
reviewed
Jan 6, 2016
| -- the same issues arise if you consider exhaustiveness checking. | ||
|
|
||
| On the other hand, it feels very silly for the compiler not to | ||
| understand that `match some_bool { true => ..., false => ... }` is |
This comment has been minimized.
This comment has been minimized.
petrochenkov
Jan 6, 2016
Contributor
I'd say true and false feel more like unit variants of "enum bool { false, true }" than opaque constants.
This comment has been minimized.
This comment has been minimized.
|
This breaks lots of code of this kind: pub struct Errno(pub c_int);
pub const Interrupted: Errno = Errno(EINTR);My code contains at least 26 instances of this pattern (excluding flags) and an uncountable number of variants:
For instance, the Errno struct comes with 132 variants. How am I supposed to repair this? |
This comment has been minimized.
This comment has been minimized.
Newtypes like this are exactly the pattern which is responsible for the most of breakage on crates.io and in rustc itself. |
This comment has been minimized.
This comment has been minimized.
|
I've also no idea what this has to do with constants. match x {
Errno(1) => 1,
_ => 0,
}
if x == Errno(1) { 1 } else { 0 }are not guaranteed to produce the same result either. Whether |
This comment has been minimized.
This comment has been minimized.
|
Of course the same also applies to enums. E.g. enum A {
X(u8),
Y,
}
impl PartialEq for A {
fn partial_cmp(&self, other: &A) -> bool { false }
}Matching will return results that are incompatible with the PartialEq results. |
This comment has been minimized.
This comment has been minimized.
|
Given that the behavior for enums is already fixed (at least the RFC doesn't suggest otherwise), and given that newtypes with all fields public are very similar to enums, it seems that the current behavior (which agrees with the enum behavior) is already the expected behavior. |
This comment has been minimized.
This comment has been minimized.
|
@mahkoh the problem here is using enums has a well specified structural equivalency, but using a constant doesn’t make the equivalency used obvious. There’s a bunch of additional reasons outlined by the RFC why structural equivalency with constants is not satisfactory. |
This comment has been minimized.
This comment has been minimized.
I see a difference between matching on a constant Looking at it like this, there is no reason to expect that match foo { Errno(1) => ... }and const C: Errno = Errno(1);
match foo { C => ... }would do the same thing, just as you would not necessarily expect that: if errno.0 == 1 { ... }and if errno == C { ... }would do the same thing. PS, I am summarizing something I wrote on the internals thread, just for reference. |
This comment has been minimized.
This comment has been minimized.
Yes, the proposal is backwards incompatible, and this is exactly the kind of cases that would no longer work. You would have to either use the feature-gate or translate such code from UPDATE: To be clear, I don't want to break code either, and I also really want constants of user-defined types to work in patterns! I don't mean to sound glib. But, based on the crater results, it does seem that we have room here to rollback to semantics everyone can agree on. This would allow us to have a productive discussion later on focusing on what the best overall semantics ought to be for constants in a pattern. |
This comment has been minimized.
This comment has been minimized.
|
I'm fine with a feature gate if it can be enabled at the point where the type is defined. E.g. #[structural_match]
pub struct Errno(pub c_int); |
This comment has been minimized.
This comment has been minimized.
By this, do you mean that the clients who are matching on |
This comment has been minimized.
This comment has been minimized.
|
@nikomatsakis Exactly. |
This comment has been minimized.
This comment has been minimized.
|
@mahkoh Hmm. It is normally something we would not allow, since if we decided to use semantic equality (for example), then there might not be an equivalent behavior to |
This comment has been minimized.
This comment has been minimized.
|
@nikomatsakis As long as the attribute is behind a feature gate, breaking it again doesn't seem to be a problem. |
This comment has been minimized.
This comment has been minimized.
|
This sort of breaking change seems like it should be a semver major bump. It's not a security, bug, or soundness fix. It's instead trying to improve reasoning about code. Not that the goal is bad, but that breakage for that goal seems unacceptable for the 1.x versions. |
This comment has been minimized.
This comment has been minimized.
|
@nikomatsakis Whether the behavior is the current one or one that uses PartialEq, the code I'm worried about behaves the same. This attribute is simply supposed to bridge the gap. Maybe call it |
This comment has been minimized.
This comment has been minimized.
Personally, I consider this a bug fix. That is, I did not expect that constants of arbitrary types should be matchable. In fact, I opened a bug about it before 1.0, but that bug was accidentally closed when I wrote a comment like "does not try to fix #20489", and hence it dropped off of my radar when triaging for "things that ought to be feature-gated before 1.0". However, clearly there is room for disagreement here. |
This comment has been minimized.
This comment has been minimized.
|
Curiously, Exhaustiveness checking is a separate orthogonal concern, it still may be better to turn it off for constants regardless of |
This comment has been minimized.
This comment has been minimized.
That is mostly true, but only if all types embedded within the struct also |
nrc
assigned
nikomatsakis
Jan 7, 2016
This comment has been minimized.
This comment has been minimized.
|
@nikomatsakis
So, yes, it was implied, that the both |
This comment has been minimized.
This comment has been minimized.
|
So, on reflection, I quite like this idea. My assumption is that this would be something like
Shall I adjust the RFC? I think so. (*) Actually, the attribute could even stay as a kind of performance optimization. That is, @pnkfelix and I have talked about the compiler recognizing when the |
This comment has been minimized.
This comment has been minimized.
|
|
This comment has been minimized.
This comment has been minimized.
|
On interaction of Variant 1. Vartiant 2. Variant 3. Some bikeshedding, names for |
This comment has been minimized.
This comment has been minimized.
|
Clean crater run finally completed. 6 regressions, as expected: https://gist.github.com/nikomatsakis/e714e4a824527e0ce5c9 |
nikomatsakis
referenced this pull request
Feb 5, 2016
Open
Restrict use of constants in patterns (RFC 1445) #31434
nikomatsakis
merged commit 120c28b
into
rust-lang:master
Feb 5, 2016
nikomatsakis
added a commit
that referenced
this pull request
Feb 5, 2016
This comment has been minimized.
This comment has been minimized.
|
Update: in a recent PR (rust-lang/rust#31277) |
This was referenced Mar 29, 2016
wictory
added a commit
to wictory/libpnet
that referenced
this pull request
Mar 30, 2016
wictory
referenced this pull request
Mar 30, 2016
Closed
Proposal to make libpnet compliant with RFC #1445 #176
This comment has been minimized.
This comment has been minimized.
mrmonday
commented
Mar 31, 2016
|
Is there a nice way to deal with this, that doesn't require a loss of type safety, or far more verbose code? libpnet has a lot of code like: #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct EtherType(pub u16);
pub const Ipv4: EtherType = EtherType(0x0800);
// ... list of constants ...
match some_ethertype {
Ipv4 => {},
Other => {}
}The easy way to solve it is to remove the type safety, which is what @wictory's pull request did initially - these constants have been specifically designed like this though to minimise errors (there are lots of magical numbers for lots of different things), whilst still allowing the ability to use random values if you explicitly qualify it. |
This comment has been minimized.
This comment has been minimized.
|
@mrmonday But that example compiles just fine (I added more variants to make sure there wasn't an exhaustiveness-related special casing). |
This comment has been minimized.
This comment has been minimized.
|
Turns out that EDIT: hang on, those attributes aren't stable, how was any of that compiling at all?! |
eddyb
referenced this pull request
Mar 31, 2016
Closed
Macro expansion bypasses #[derive_*] stability checks. #32655
This comment has been minimized.
This comment has been minimized.
|
@eddyb sigh. I can readily fix the code to have two distinct attributes, I was just being lazy. |
This comment has been minimized.
This comment has been minimized.
|
Although, triggering on |
nikomatsakis commentedJan 6, 2016
Feature-gate the use of constants in patterns unless those constants have simple types, like integers, booleans, and characters. The semantics of constants in general were never widely discussed and the compiler's current implementation is not broadly agreed upon (though it has many proponents). The intention of adding a feature-gate is to give us time to discuss and settle on the desired semantics in an "affirmative" way.
Because the compiler currently accepts a larger set of constants, this is a backwards incompatible change. This is justified as part of the "underspecified language semantics" clause of RFC 1122. A crater run found 14 regressions on crates.io, which suggests that the impact of this change on real code would be minimal.
Note: this was also discussed on an internals thread. Major points from that thread are summarized either inline or in alternatives.
Rendered view.