Why can you use different unconstrained lifetimes to implement traits?

I was surprised that this has always worked:

struct User(usize);
struct UserProtoBuf(usize);
impl<'a> From<&'a User> for UserProtoBuf {
    fn from<'b>(user: &'b User) -> Self {
        UserProtoBuf(user.0)
    }
}

I would have expected that it needed to be

impl<'a> From<&'a User> for UserProtoBuf {
    fn from(user: &'a User) -> Self {
        UserProtoBuf(user.0)
    }
}

So that the from method matched the expected type exactly.

But I guess since it’s unconstrained it’s fine?

It’s interesting that, with IHLE, this means you can just write the following and it works:

impl From<&User> for UserProtoBuf {
    fn from(user: &User) -> Self {
        UserProtoBuf(user.0)
    }
}

So maybe it’s good even if it feels weird to me?

This looks correct to me. It is the same behavior at play as in the following minimized form:

trait Trait {
    fn f(arg: &'static u8);
}

struct S;
impl Trait for S {
    fn f(_arg: &u8) {}
}

The relevant behavior is that trait impls are allowed to provide functions that are more general than what is required by the trait. In my code, the trait requires fn f<'a>(arg: &'a u8) where 'a: 'static, and the trait impl for S has dropped the where-clause 'a: 'static to provide the more general implementation fn f<'a>(arg: &'a u8).

In your top snippet, the trait From<&'a User> requires a function with the signature fn from(user: &'a User) -> Self i.e. fn from<'from>(user: &'from User) -> Self where 'from: 'a. The trait impl for UserProtoBuf drops that where-clause and provides the more general implementation signature fn from<'b>(user: &'b User) -> Self which can accept an arbitrarily short 'b.

3 Likes

Thanks, @dtolnay. Seems like this behaviour goes all the way back to Rust 1.0.

I’ve gone and made a PR to use it to simplify a bunch of things, such as

-    impl<'a, 'b, A: ?Sized, B: ?Sized> PartialEq<&'b mut B> for &'a mut A where A: PartialEq<B> {
+    impl<A: ?Sized, B: ?Sized> PartialEq<&mut B> for &mut A where A: PartialEq<B> {
         #[inline]
-        fn eq(&self, other: &&'b mut B) -> bool { PartialEq::eq(*self, *other) }
+        fn eq(&self, other: &&mut B) -> bool { PartialEq::eq(*self, *other) }
         #[inline]
-        fn ne(&self, other: &&'b mut B) -> bool { PartialEq::ne(*self, *other) }
+        fn ne(&self, other: &&mut B) -> bool { PartialEq::ne(*self, *other) }
     }
1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.