Std implementations of PartialOrd are violating the conditions regarding transitivity and symmetry.
From the standard library docs:
The comparison must satisfy, for all a , b and c :
- transitivity:
a < b and b < c implies a < c . The same must hold for both == and > .
- duality:
a < b if and only if b > a .
Note that these requirements mean that the trait itself must be implemented symmetrically and transitively: if T: PartialOrd<U> and U: PartialOrd<V> then U: PartialOrd<T> and T: PartialOrd<V> .
(emphasis mine)
focus on the final paragraph in the quote above and look at the following example
use std::path::Path;
use std::ffi::OsStr;
let c: &str = "c";
let b: &OsStr = "b".as_ref();
let a: &Path = "a".as_ref();
assert!(b < c); // works
// assert!(c > b); // doesn’t compile
assert!(a < b && b < c); // works
// assert!(a < c); // doesn’t compile
(in the playground)
So either the library documentation is off or the implementations are flawed.
And the transitivity requirements are impossible hard to ensure in a multi-crate ecosystem anyways.
Note that, technically, it’s impossible to enforce transitive existence of impls for the trait unless using operands of fully “external” types is completely prohibited:
If I’m writing a crate foo providing a type struct Foo(…); and an impl PartialOrd<i32> for Foo as well as an impl PartialOrd<Foo> for i32, it seems like I’m following the rules set by PartialOrd’s documentation.
If I’m writing a crate bar providing a type struct Bar(…); and an impl PartialOrd<i32> for Bar as well as an impl PartialOrd<Bar> for i32, it seems like I’m following the rules set by PartialOrd’s documentation.
Now, if I’m writing a third crate that imports both foo and bar, then I’ll have impl PartialOrd<Foo> for i32 as well as impl PartialOrd<i32> for Bar, but obviously impl PartialOrd<Foo> for Bar is missing.
In this example, the crates foo and bar each provided an impl where one of the operands, i32, was a type that’s fully external to the crate itself (in the sense that neither the type nor any of its generic arguments are part of foo, or part of a different crate that has the same owners as foo [i32 has no generic arguments to begin with]).
@rustbot label C-bug, T-libs
Std implementations of PartialOrd are violating the conditions regarding transitivity and symmetry.
From the standard library docs:
(emphasis mine)
focus on the final paragraph in the quote above and look at the following example
(in the playground)
So either the library documentation is off or the implementations are flawed.
And the transitivity requirements are
impossiblehard to ensure in a multi-crate ecosystem anyways.Note that, technically, it’s impossible to enforce transitive existence of impls for the trait unless using operands of fully “external” types is completely prohibited:
If I’m writing a crate
fooproviding a typestruct Foo(…);and animpl PartialOrd<i32> for Fooas well as animpl PartialOrd<Foo> for i32, it seems like I’m following the rules set byPartialOrd’s documentation.If I’m writing a crate
barproviding a typestruct Bar(…);and animpl PartialOrd<i32> for Baras well as animpl PartialOrd<Bar> for i32, it seems like I’m following the rules set byPartialOrd’s documentation.Now, if I’m writing a third crate that imports both
fooandbar, then I’ll haveimpl PartialOrd<Foo> for i32as well asimpl PartialOrd<i32> for Bar, but obviouslyimpl PartialOrd<Foo> for Baris missing.In this example, the crates
fooandbareach provided animplwhere one of the operands,i32, was a type that’s fully external to the crate itself (in the sense that neither the type nor any of its generic arguments are part offoo, or part of a different crate that has the same owners asfoo[i32has no generic arguments to begin with]).@rustbot label C-bug, T-libs