How do I fix this lifetime error?

Essentially, what I'm trying to do is make a trait that returns an owned value that may contain a reference to self, and then use implementations of that trait as key functions to implement traits such as PartialEq. This is what I've got after struggling for a few hours, and I have no idea why it doesn't compile. What I'm trying to do should be possible, but I haven't been able to figure out how to properly express it.

use core::marker::PhantomData;

struct Key<T, U>(T, PhantomData<U>);

trait GetKey<'a, T: 'a> {
    fn key(t: &'a Self) -> T;
}

impl<'a, T: GetKey<'a, U>, U: 'a + PartialEq> PartialEq for Key<T, U> {
    fn eq(&self, other: &Self) -> bool {
        GetKey::key(&self.0) == GetKey::key(&other.0)
    }
}

impl<'a> GetKey<'a, &'a i32> for i32 {
    fn key(t: &'a Self) -> &'a i32 {
        t
    }
}

fn main() {
    println!("{}", Key(4, PhantomData) == Key(4, PhantomData));
}

Here's one way. You could get rid of the Clone bound by having your own "maybe a reference, maybe not" struct.

This isn't really what you were aiming for though, as far as I understand. It makes "is this a reference or not" into a dynamic check. What you want feels like a GAT situation. Hmm, this just recently came up elsewhere... let's try something like that.

This is how far I got on that road. The error looks like a normalization behind references shortcoming. I believe that is this issue and that it is being worked on here. You may note that the latter fixes a number of ICEs (including unlinked ones that have been closed as duplicates); when I ran into this before, it was due to getting an ICE after trying to work around the normalization issues.

Maybe someone else knows how to work around it (without an ICE) in this case.

Do you want to have multiple GetKey implementations for the same type? E.g. use Key<i32, &i32> but also Key<i32, &u32> etc…? Or is an associated type (potentially generic) like @quinedot suggested an appropriate solution?

I'm good with just one implementation per type, I only used a generic like that because it wasn't happy with what it called an unused lifetime when I made it an associated type. In a strange way, it kinda feels good that the reason I couldn't make it work was likely because of a problem in the compiler. I felt like I was going crazy trying to make sense of all the errors.

Alright… I’m giving this a try myself now. This didn’t work (playground).

Moving the PartialEq onto the Key type itself works (playground) but I’m quite certain that this is not what you wanted, because it sounds like PartialEq should be one of many traits to consider, so having it baked in feels wrong.

Alright, next try, introducing some helper traits… actually… let’s first get rid of the GATs (playground, same problem as before), and now a significant amount of extra traits, but all of which are implemented by blanket impls (so there’s no overhead for the user of the GetKey trait infrastructure and Key<T> type). This all to be able to write T: HasKeyPartialEq instead of T: GetKey, for<'a> KeyType<'a, T>: PartialEq in a way that the compiler happily notices when the former is actually implemented … i.e. the compilation error is gone. However, we cannot directly call == on the result of GetKey::key anymore. There’s two ways around them, both using subtle type tricks to exploit the TypeEqual trait.

Either you can exploit TypeEqual in the PartialEq impl directly like so, or you can add another key_partial_eq method to the GetKeyPartialEq trait that uses the original key method with a similar TypeEqual usage in the blanket impl, like this.

Hope you can make some sense of my code :sweat_smile:


Edit: In case it isn’t clear, the trait GetKeyPartialEq it more of a convenience thing only. The crucial thing to make the compiler happy is replacing the for<'a> <T as HasKey<'a>>::Key: PartialEq by for<'a> T: HasKeyPartialEq<'a>. Here’s a playground without the GetKeyPartialEq trait.

1 Like

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.