-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Should PartialOrd and Ord impls be equivalent? #41270
Comments
For the record, the PR that changed this is #39538. |
For concreteness, on play.rust-lang.org, the following code succeeds under stable, and panics on nightly. It defines a partially ordered use std::cmp::Ordering;
#[derive(Debug, Eq, PartialEq, Ord, Copy, Clone)]
struct Coord {
x: u32,
y: u32,
}
impl PartialOrd for Coord {
fn partial_cmp(&self, other: &Coord) -> Option<Ordering> {
if self.x == other.x && self.y == other.y { Some(Ordering::Equal) }
else if self.x <= other.x && self.y <= other.y { Some(Ordering::Less) }
else if self.x >= other.x && self.y >= other.y { Some(Ordering::Greater) }
else { None }
}
}
fn main() {
let c1 = Coord { x: 1, y: 2 };
let c2 = Coord { x: 2, y: 1 };
let mut v1 = vec![c1, c2];
let mut v2 = vec![c2, c1];
v1.sort();
v2.sort();
assert_eq!(v1, v2);
} |
Nominating since this can probably be classified as either a breaking change or a regression. |
Ord says Trait for types that form a total order. and defines this in terms of the comparison operators. partial_cmp says This method returns an ordering between self and other values if one exists.; and the ordering does exist if the type is This relationship should be clearer in the docs. |
If this interpretation is correct, this line from the
For what it is worth, I think it is totally reasonable to allow Can you say why it is important to unify the two, when both exist? The only reason I can see is that the assumption has existed for a while, and someone would have to audit the code for |
Also, the requirement that |
It's also totally reasonable that the assertion in your example program's assertion fails on nightly. PartialOrd's docs say it is “Trait for values that can be compared for a sort-order.” and your vector is being sorted in a way that is consistent with PartialOrd. It is unreasonable if
Yes, the design doesn't really take anything more than floats into account. I don't think PartialOrd is designed to be useful on its own. It's a crutch for floating point numbers, and a way to separate them out from the regular totally ordered types. Floats are one example of a type that could implement a total order, different from its partial order, but the trait |
Yup, I think both are reasonable. I was reacting to your claim that it is unreasonable any other way. :)
I agree that it can be confusing. However, it gives you access to the < operator because Can I propose an alternate, less confusing design if |
Derives are recipes that are good for common cases but not for every case. It's often wrong to request derive(Ord) when implementing PartialOrd manually — and that could be a warning. |
If we imagine that |
I think it should be the |
The libs team discussed this at triage yesterday and we believe that this issue is the same as #40192, which we closed with the intention of "PartialOrd must behave the same as Ord, if implemented". It sounds like though we definitely need to update the docs! @frankmcsherry out disposition was to stay the current course (while updating docs), but have you found it difficult to update the code you have locally? If that takes awhile we don't mind considering backing out the change temporarily to reevaluate. One reason we're tempted to continue to require that Ord/PartialOrd are equivalent is along the lines of what @bluss mentioned above, different parts of the standard library will react differently if they behave differently. The intent is that they're used interchangeable on |
I'm planning on removing I think the main frustrating thing is going to be auditing code to make sure I don't use Edit: I have no idea if it is a breaking change, but adding a default implementation for |
Ok in that case I believe that the outstanding issue here to be fixed is to clarify the documentation of @frankmcsherry if you find it difficult to upgrade though please let us know! |
I'll let everyone know, not to worry. ;) |
Next step here is to update the documentation for both PartialOrd and Ord to indicate that the two must agree on the ordering of any types they are implemented on. |
What about the relationship between Edit: wrong link. |
@Rufflewind Which example? There are a lot of code in that page. |
@kennytm My link was incorrect. Here’s the correct one: https://doc.rust-lang.org/std/collections/binary_heap/#examples (“This is a larger example that implements Dijkstra's algorithm to solve the shortest path problem on a directed graph.”) |
@Rufflewind That's a great find in the Yes, the answer is that More concretely, here's what is I suggest changing:
Anything else I'm missing here? @brson? |
I think Eq can still be derived since it has no methods. |
…ent, r=alexcrichton Docs: impls of PartialEq/PartialOrd/Ord must agree Fixes rust-lang#41270. This PR brings two improvements to the docs: 1. Docs for `PartialEq`, `PartialOrd`, and `Ord` clarify that their implementations must agree. 2. Fixes a subtle bug in the Dijkstra example for `BinaryHeap`, where the impls are inconsistent. Thanks @Rufflewind for spotting the bug! r? @alexcrichton cc @frankmcsherry
…ent, r=alexcrichton Docs: impls of PartialEq/PartialOrd/Ord must agree Fixes rust-lang#41270. This PR brings two improvements to the docs: 1. Docs for `PartialEq`, `PartialOrd`, and `Ord` clarify that their implementations must agree. 2. Fixes a subtle bug in the Dijkstra example for `BinaryHeap`, where the impls are inconsistent. Thanks @Rufflewind for spotting the bug! r? @alexcrichton cc @frankmcsherry
Nightly introduces a change that alters the behavior of sorting, by using the
PartialOrd
methodslt
,le
and such, rather thanOrd
scmp
method.This has implications for me, on account of I am a weirdo with types that implement both
PartialOrd
andOrd
, with the property thatOrd
is a strict refinement ofPartialOrd
. That is, wheneverpartial_cmp
returns a non-None
value thencmp
returns the same value, but there are incomparable elements of the partial order that are ordered byOrd
. This sounds like a mess, but is important for doing things like efficient deduplication (viasort(), dedup()
). For example, I have pairs of integers that are partially ordered (via product order), and I need to be able to deduplicate them.I think this is a "breaking change" in that code that previously did one thing now does another thing. It may be that it is intended, and that all implementations of
PartialOrd
not equivalent to theirOrd
implementation were in error and get no love, but (i) it would be great to have someone actually state that, and (ii) thePartialOrd
docs could use some sprucing up (they currently say thatPartialOrd
is for defining sort orders, which is ...).I can fix all of my code, by moving all the
PartialOrd
functionality into theLattice
trait I already have, but I don't see why anOrd
implementation should imply a totalPartialOrd
implementation (you should add a default implementation ofcmp
that unwrapspartial_cmp
in that case).Edit bluss pointed me at #12517 which discusses the naming and stuff, but I couldn't see that the discussion came to a conclusion on whether
PartialOrd
must be equivalent toOrd
when it exists.The text was updated successfully, but these errors were encountered: