With this question I am looking for feedback from people who have more knowledge in this area. I am by no means an expert. So I might as well ask my question upfront: Is my reasoning correct here?
The problem
Based on the answer to a question here on SO, I was confused to see the lifetime elided in the implementation of a trait method:
impl<'a, 'b, T> PartialEq<RefEquality<'b, T>> for RefEquality<'a, T> {
fn eq(&self, other: &RefEquality<T>) -> bool {
self.0 as *const T == other.0 as *const T
}
}
Here, in the method signature the lifetime 'b
was omitted on the type of other
. This works and is correct. I expected it to be &RefEquality<'b, T>
for the type to be correct. After all, the 'b
here is essential: The lifetime has to be different from 'a
. If not, it would be too restrictive: The implementation would only work for another RefEquality<T>
with the same lifetime as Self
. So those are obviously different semantics. How can the compiler infer the correct lifetime?
Lifetime elision takes care of it
Lifetimes on function signatures can be elided but they cannot be elided on impl blocks. There, the types have to be fully specified which includes naming lifetimes.
On the eq()
method on the other hand, I am able to elide the lifetime in the type annotation of other. In fact, the compiler then inserts an arbitrary lifetime for it which is obviously different from 'a
. That is the reason why this works while also keeping the same semantics:
impl<'a, 'b, T> PartialEq<RefEquality<'b, T>> for RefEquality<'a, T> {
fn eq<'c>(&self, other: &RefEquality<'c, T>) -> bool {
self.0 as *const T == other.0 as *const T
}
}
Here, I introduced an arbitrary lifetime 'c
for the method, which is basically the same the compiler does in case of lifetime elision.
Naming a lifetime 'b
in my trait impl just stated that it has to be different from 'a
(I also not linked them in any way). It follows logically, that this does not work:
impl<'a, 'b, T> PartialEq<RefEquality<'b, T>> for RefEquality<'a, T> {
fn eq(&self, other: &RefEquality<'a, T>) -> bool {
self.0 as *const T == other.0 as *const T
}
}
I said in on the impl the types would be different (based on their lifetimes) but now the actual eq()
implementation says they are the same. This results in a type error as expected.
What if I want the lifetimes to be equal? Can I still use lifetime elision in this case, or will the compiler insert an arbitrary lifetime and report a type error? It turns out, the inference works correctly here as well:
impl<'a, T> PartialEq<RefEquality<'a, T>> for RefEquality<'a, T> {
fn eq(&self, other: &RefEquality<T>) -> bool {
self.0 as *const T == other.0 as *const T
}
}
The elided lifetime will be inferred to be 'a
, keeping the desired semantics that both RefEquality<T>
types have to have the same lifetime.