Skip to content

Std implementations of PartialOrd are violating the conditions regarding transitivity and symmetry. And the transitivity requirements are i̶m̶p̶o̶s̶s̶i̶b̶l̶e̶ hard to ensure in a multi-crate ecosystem anyways. #87067

Closed

Description

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

Labels

A-docsArea: documentation for any part of the project, including the compiler, standard library, and toolsC-bugCategory: This is a bug.T-libs-apiRelevant to the library API team, which will review and decide on the PR/issue.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions