Auto referencing in rhs of comparisons

I am confused why this code compiles:

#[derive(PartialEq, PartialOrd, Eq, Ord, Debug)]
struct Foo {
    a: i64,
}

fn main() {
    let x = Foo { a: 42 };
    let y = Foo { a: 100 };
    let b = x < y;
    println!("{:?}, {:?}, {:?}", x, y, b);
}

After reading the Rust Book I would have thought the code would have to look like this (does not compile):

#[derive(PartialEq, PartialOrd, Eq, Ord, Debug)]
struct Foo {
    a: i64,
}

fn main() {
    let x = Foo { a: 42 };
    let y = Foo { a: 100 };
    let b = x < &y; <--- mismatched types, expected `Foo`, found `&Foo`
    println!("{:?}, {:?}, {:?}", x, y, b);
}

The reason I believe the bottom code is "correct" is because of the signature of the partial_cmp method. If we were to call it directly we would have to call it with x.partial_cmp(&y). The docs on PartialEq also say that

The lt , le , gt , and ge methods of this trait can be called using the < , <= , > , and >= operators, respectively.

But those operators clearly don't follow the same signature given the snippets above. Also the syntax that compiles is making it look like the compiler is failing to invalidate y after it is "moved" into the < call. Clearly it is not actually being moved but the syntax is failing to demonstrate that because there is no reference.

Is there a name for this magic? Where else does this happen?

The operators that call traits don't use the same rules as method call lookup; they turn directly into trait lookups. Supposing that x has type X and y has type Y, x < y desugars into a call to exactly:

<X as PartialOrd<Y>>::lt(&x, &y)

So,

  • the operator will compile if and only if there's a PartialEq implementation for the types of its operands.
  • Comparing references works not because of autoderef/autoref, but because of the implementation of PartialEq for references.
  • Unlike method calls, the left side and the right side follow exactly the same rules.
1 Like

(On mobile and can't do a proper exploration right now but) probably not exactly.[1] I've learned to never trust documentation saying a Rust desugaring is "equivalent to" something else.

Notionally or approximately though, yes.


  1. The differences for that case are sort of buried, sorry. PartialEq is definitely different too. ↩ī¸Ž

I should have specified "exactly this trait impl" instead of "exactly this code".

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.