Life time in trait bound

The following codes cannot compile:

trait ForCalc {
    type A<'a>;
}

trait ToCalc {}

impl<'a, T, M> ToCalc for T
where
    T: ForCalc<A<'a> = M>,
{}

error[E0207]: the type parameter `M` is not constrained by the impl trait, self type, or predicates
  --> src\cond.rs:18:13
   |
18 | impl<'a, T, M> ToCalc for T
   |             ^ unconstrained type parameter

But this can:

trait ForCalc {
    type A;
}

trait ToCalc {}

impl<T, M> ToCalc for T
where
    T: ForCalc<A = M>,
{}

Why does this happend?

Head up: This is me walking myself through it. I don't think it's documented for GATs anywhere (but would love to be proven wrong).


Let's look at the definition of "constrained", and apply it to each case.

impl<T, M> ToCalc for T
where
    T: ForCalc<A = M>,
  • T: Is constrained as T appears in the Self type
  • M: Is constrained as
    • <T as ForCalc>::A == M is a clause
    • T as ForCalc is not this impl
    • T is constrained

The idea here is that at some invocation site where T and the trait ToCalc is known, we can determine all the other parameters. We know T, so we can find T's impl of ForCalc (as it too must be findable), and that impl must have exactly one value for the associated type A, and from that we can find M.


Next case:

impl<'a, T, M> ToCalc for T
where
    T: ForCalc<A<'a> = M>,
{}
  • T: Is constrained as T appears in the Self type
  • 'a: Is not contrained (see below)
  • M: Is not constrained as
    • While <T as ForCalc>::A<'a> == M is a clause
    • And <T as ForCalc> is not this impl
    • And T is constrained,
    • 'a is not constrained
      • So we can't figure out what M is

That RFC predates GATs, but the lifetime being unconstrained is mentioned in this issue.

Here's me spitballing: 'a isn't an output of the implementation of ForCalc. So we can still find impl ForCalc for T from T, but we still don't know what lifetime we're talking about. So we can't figure out M either. Maybe ForCalc looks like this for example:

trait ForCalc {
    type A<'a>;
    fn uses_the_gat(&self, a: A<'_>) -> &'a str;
    fn does_not_use_the_gat(&self);
}

If we're somewhere else in code and have:

t.does_not_use_the_gat();

Then if method resolution is unambiguous, we have found

  • t: T
  • And the method is ForCalc::does_not_use_the_gat

But there is no way to decide what 'a might be, so there is no way to find what M might be, so we can't figure out which exact implementation (with all generic parameters resolved) to use.

Or in short, for a lifetime to be constrained it has to be part of the implementor, part of the trait, or part of a non-generic associated type... so just being the input to a GAT is not enough.

Make sense?


The linked issue says:

the general gist is to be able to do something like for<'b> T: AsRef2<Output<'b> = &'b [U]>, we need some sort of implied bounds or additional reasoning.

In other words, sort of like your last thread, you need some way to formulate a rule that always applies. That way you don't need to find the 'a, you can use any 'a.


If you have an actual failing use-case you'd like to try and fix, feel free to supply it. Presumably your actual implementation cares about M in some way.

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.