PartialEq, PartialOrd: update and synchronize handling of transitive chains - #115386
Conversation
|
r? @m-ou-se (rustbot has picked a reviewer for you, use r? to override) |
|
There was another comment posed here that seemingly got removed again, starting with
Note that when considering semver-compatible evolution of crates, this is already the case today. The alpha/beta example in the docs doesn't need the new "chain" part of transitivity, it already works on today's published docs. (Generally please don't remove comments, that kind of rewriting of history makes discussions very confused to follow later, or to follow the email notification chain. Just edit or post a 2nd reply to explain why you changed your mind.) |
dtolnay
left a comment
There was a problem hiding this comment.
We discussed this PR in today's T-libs-api meeting and were skeptical that the new content (neither the clarification on transitivity nor the new paragraph regarding cross-crate impls) would have a practical consequence on people writing more correct PartialEq impls.
My personal view: I can see that the existing definition is not bulletproof, but if I imagine a hypothetical bulletproof definition or something in that direction (such as this PR), it is not obvious to me that a PartialEq impl which does not conform to the new definition is more likely to mean the impl is wrong or the new definition is bad. This significantly reduces my interest in pinning down a more precise definition of what makes a PartialEq impl correct. I think the intended use for PartialEq conveyed by the current docs, and the remaining wiggle room for impls, is pretty good.
|
So, the docs are ambiguous but there is no intent to fix them? That's the worst possible outcome. :( It runs a high risk of people interpreting the docs in different ways without even realizing that they are deliberately ambiguous. Note that these questions about transitivity come up for real, see e.g. here and here. The current documentation is not answering the questions people have. |
8aebbc1 to
f2719e9
Compare
|
I have removed the new paragraph about dealing with the multi-crate situation. Now all this PR does is un-do a likely accidental side-effect of #81198: before that PR, it was the case that if It is true that this transitivity requirement cannot be upheld on a per-crate level. However, that is already the case for the transitivity requirement that we have documented right now. In other words, the docs are already somewhat aspirational when it comes to multi-crate situations. Therefore I think we really should document that, ideally, this property holds for longer chains as well. #118108 does the same thing for Re-nominating due to the changed PR contents and the new information (this un-does a likely accidental change, bringing us closer to how things were documented before 2021, rather than introducing something completely new). |
|
What about this situation?
In json, there is no distinction between integers and floats. Having both a In cbor, there is a distinction between integers and floats. Having both a But that allows for:
Which would break the proposed chaining rule. Does that mean there's a problem with one of the PartialEq implementations in this example, or with the proposed rule? |
|
Similar problems already arise with the rule as documented today: assuming both JsonValue and CborValue can be compared with both i32 and i64 (and assuming CborValue are inequal whenever the type differs). Now imagine we wanted to add a Arguably libraries shouldn't "connect" two outside-the-crate types by equality that are not already connected. The moment they do, they are defining a notion of equality on a pair of types they do not control, and different libraries could define conflicting notions of equality. So, I don't have a solution to this issue, but it's not an issue introduced by this PR. The issue was introduced by #81198. |
|
@RalfJung Some additional thoughts on that issue: If The rule is about Does that mean that a |
| /// - **Transitive**: if `A: PartialEq<B>` and `B: PartialEq<C>` and `A: | ||
| /// - **Transitivity**: if `A: PartialEq<B>` and `B: PartialEq<C>` and `A: | ||
| /// PartialEq<C>`, then **`a == b` and `b == c` implies `a == c`**. | ||
| /// This must also work for longer chains, such as when `A: PartialEq<B>`, `B: PartialEq<C>`, |
There was a problem hiding this comment.
| /// This must also work for longer chains, such as when `A: PartialEq<B>`, `B: PartialEq<C>`, | |
| /// This should also work for longer chains, such as when `A: PartialEq<B>`, `B: PartialEq<C>`, |
Attempting to account for the state of the ecosystem, here.
There was a problem hiding this comment.
Should the "must" above this enumeration also become a "should"? Or do you intentionally make a difference between the "basic" transitivity case and the cases involving longer chains (or symmetry, as per Mara's point)?
There was a problem hiding this comment.
@RalfJung It's possible that the other "must"s here should also become "should"s, in practice, though longer chains are more likely to fail these properties. De facto, the ecosystem is going to continue to provide impls that don't satisfy all of these properties, and as a result, code can't have any definitive reliance on these properties. "should" acknowledges that these properties are more on the "try not to confuse the humans reading your code" side than the "allow computers to reason about your code" side.
There was a problem hiding this comment.
as a result, code can't have any definitive reliance on these properties
We already document that "Violating these requirements is a logic error. The behavior resulting from a logic error is not specified, but users of the trait must ensure that such logic errors do not result in undefined behavior. This means that unsafe code must not rely on the correctness of these methods." In other words, we already expect a certain amount of resilience against these properties being violated.
I guess the open question is whether
- we make this "must" and violating them is therefore declared a bug (albeit one that can, at worst, cause panics or logic misbehavior, not UB)
- we make this "should" and... well I guess I am not sure what that would mean? Is now the code that relies (to the extent permitted by the docs) on the property the one that is buggy? Or is the conclusion that sometimes things just don't compose and we don't want to take a stance on where the bug lies in that case?
My personal preference would be to make this a "must" in both cases. But I could live with a "should" as well. Until we have explicit RFC-style policies for what "must" and "should" mean, this is all a bit fuzzy anyway.
There was a problem hiding this comment.
In other words, we already expect a certain amount of resilience against these properties being violated.
💯
In my opinion "must", together with the existing documentation that violating is a logic error, captures the right intent better than changing to "should".
|
@m-ou-se Hm, that is a good question. Before #81198, if we had So it would only be consistent to allow the transitive-chain rule to also use allow using comparisons "backwards", i.e. to require that if |
I agree, but that would mean that it'd be wrong to have both I don't think that is something we can go forward with. It is very common for an |
|
The final comment period, with a disposition to merge, as per the review above, is now complete. As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed. This will be merged soon. |
|
@dtolnay can I take your starting the FCP process as "r=me once FCP finishes"? |
|
@bors r+ |
|
🌲 The tree is currently closed for pull requests below priority 100. This pull request will be tested once the tree is reopened. |
PartialEq, PartialOrd: update and synchronize handling of transitive chains It was brought up in https://internals.rust-lang.org/t/total-equality-relations-as-std-eq-rhs/19232 that we currently have a gap in our `PartialEq` rules, which this PR aims to close: > For example, with PartialEq's conditions you may have a = b = c = d ≠ a (where a and c are of type A, b and d are of type B). The second commit fixes rust-lang#87067 by updating PartialOrd to handle the requirements the same way PartialEq does.
It was brought up in https://internals.rust-lang.org/t/total-equality-relations-as-std-eq-rhs/19232 that we currently have a gap in our
PartialEqrules, which this PR aims to close:The second commit fixes #87067 by updating PartialOrd to handle the requirements the same way PartialEq does.