Confusing error message when trait is impl'd on &T and bounds are not satisfied

33dbd34
Opened by Mikko Rantanen at 2023-06-23 17:07:02

Example code: https://play.rust-lang.org/?gist=6e7c1a4ad58c82717271ad4d5cef1181&version=stable

The form that I encountered was:

error[E0277]: the trait bound `&Ident: std::cmp::PartialEq<str>` is not satisfied
  --> src/main.rs:22:18
   |
22 |         if ident.eq( "STRING" ) {
   |                  ^^ can't compare `&Ident` with `str`
   |
   = help: the trait `std::cmp::PartialEq<str>` is not implemented for `&Ident`

syn::Ident implements PartialEq<T> where T: AsRef<str> so I know I can compare Ident with a str.

My first reaction was confusion with str. It's so rare to see str alone that my first instinct was to ensure that what I have is &str. While still confused, I decided to throw an & in front of my parameter. To my surprise that fixed the error!

... but now I was even more confused. ident.eq( &"Static string" ) looks a bit weird - and why would it even make a difference?

After a while I realized that my ident was a ref of &Ident - so essentially a double-borrowed &&Ident. But as Rust has auto-(de)ref for method calls, this shouldn't matter. I've had &&&...Foo types in the past by being overly eager with &'s and these have worked just fine without needing any manual *'s.

However I've now concluded that the problem here is the built-in impl PartialEq<&B> for &A.

With let ident : &&Ident, the ident.eq(..) gets resolved to:

impl<'a, 'b, A (&Ident), B = ?>PartialEq<&B> for &A
where A (&Ident) : PartialEq<B> {
    fn eq( &self, other: &B ) -> bool;
}

Where B ends up as str to make the eq(..) signature match &str. Thus the error.

In any case the biggest issue here is the "friendly" error message, I feel: can't compare '&Ident' with 'str'. Both types here seem wrong.

  • On the right side as far as I can tell, "STRING" is &str, not str.
  • On the left side the type should be &&Ident, not an &Ident.

I guess the parameters are wrong, because Rust does one cycle of derefs. The original types are &&Ident and &str, but the impl above drops one & from each of these.

What purpose does the PartialEq<&B> for &A impl serve anyway? If that impl didn't exist, wouldn't rustc do derefs for &&&A until it found impl for A? Then it would deref the argument. Or is this unwanted behavior in this scenario - even if this is how the auto-deref works everywhere else? Or ~~am I missing~~ I am probably missing some other detail that prevents this from working.

  1. Current error:

    error[[E0277]](https://doc.rust-lang.org/stable/error_codes/E0277.html): can't compare `&S` with `S`
      --> src/main.rs:29:15
       |
    29 |     (&&s).eq( &o );  // This fails. Trait resolution doesn't auto deref?
       |           --  ^^ no implementation for `&S == S`
       |           |
       |           required by a bound introduced by this call
       |
       = help: the trait `PartialEq<S>` is not implemented for `&S`
    help: consider removing the leading `&`-reference
       |
    29 -     (&&s).eq( &o );  // This fails. Trait resolution doesn't auto deref?
    29 +     (&&s).eq( o );  // This fails. Trait resolution doesn't auto deref?
       |
    
    For more information about this error, try `rustc --explain E0277`.
    

    Dylan DPC at 2023-06-23 17:07:02