Weird issue with lifetimes and associated types


#1

I seem to have come across a rather strange interaction between associated types, lifetimes, and where clauses on methods. Playground

I was able to reduce the original code to this:

trait Trait {
    type Associated;

    // In the original, this method took parameters and was generic, and the
    // constraint here was dependent on those generics, so this can't just be
    // moved to `type Associated : Default`.
    fn method() where Self::Associated : Default;
}

struct Struct<'a, 'b>(&'a (), &'b ());

// Problem here
// Changing `'b: 'a` to just `'b` resolves the compiler error.
// Note that in the real code, there actually is a need for this lifetime
// constraint (and it's present on `Struct` as well).
impl<'a, 'b: 'a> Trait for Struct<'a, 'b> {
    type Associated = ();

    fn method() { }
}

fn main() { }

which produces the incredibly unhelpful errors

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'b` due to conflicting requirements
  --> <anon>:19:5
   |
19 |     fn method() { }
   |     ^^^^^^^^^^^^^^^
   |
help: consider using an explicit lifetime parameter as shown: fn method()
  --> <anon>:19:5
   |
19 |     fn method() { }
   |     ^^^^^^^^^^^^^^^

error: aborting due to previous error

(This was on nightly, but essentially the same issue also appears on beta and stable.)

The error is dependent on both the lifetime constraint 'b: 'a (which in the original code was also on the struct declaration) and the where Self::Associated constraint in the trait (which was also replicated to the impl method in the original). Removing either of those makes the code compile, even though there’s no way obvious to me for the two to interact.

I’d appreciate any light that could be shed on this; I’m honestly not sure whether there’s actually something wrong with the code or if I’ve run into some odd corner case in the compiler.


#2

Looks like a bug to me. At the very least the compiler should give more information in the error message. This particular error generally includes the two actual conflicting requirements.


#3

Thanks for the second opinion. Filed https://github.com/rust-lang/rust/issues/35624.

In my particular case, I was able to work around the issue by hoisting the associated type to a type parameter on the trait, like

trait Trait<A> {
  fn method() where A : Default;
}

This of course brings its own problems, such as proliferation of type parameters just to be able to refer to the trait and needing to use PhantomData in a lot of places, but it basically works otherwise.