Impl trait with more restrictive lifetime

I'm trying to implement a trait with a method that has an existing lifetime, for a struct that also has a lifetime.

trait MyTrait {
    fn some_fn<'a>(&'a mut self, s: &'a String);
}

struct MyStruct<'a>(Option<&'a String>);

I need the lifetime of the trait method to outlive the lifetime of the struct, but I'm struggling with that.
Adding a 'b: 'a trait bound makes the compiler complain that it doesn't match the trait, since the trait doesn't have that constraint. And removing the bound causes the lifetimes to be unrelated, and hence I can't assign s = self.0

impl<'a> MyTrait<'a> for MyStruct<'a> {
    fn some_fn<'b>(&'b mut self, s: &'b String)
    where:
        'b: 'a
    {
        self.0 = Some(s);
    }
}

Anyone have any pointers?
Thanks!

So if we take the reduction of what you're trying to do, it is basically this:

impl<'a> MyStruct<'a> {
    fn some_fn<'b>(&'b mut self, s: &'b String)
    where:
        'b: 'a
    { .. }
}

If we desugar the method signature, we get this:

    fn some_fn<'b>(self: &'b mut MyStruct<'a>, s: &'b String)
    where:
        'b: 'a

The nested lifetime (&'b mut MyStruct<'a>) implies 'a: 'b, because otherwise the reference would point to something invalid. The compiler makes use of this implied bound (e.g. you can take advantage of the bound within the function body).

In combination with the explicit bound 'b: 'a, this means that the two lifetimes must actually always be the same. So we can rewrite the method like so:

    fn some_fn(self: &'a mut MyStruct<'a>, s: &'a String)

And that's not actually something you want to do.


So what's the actual goal here? It's possible you're trying to create a self-referencial struct for example (a common reason to end up trying to borrow yourself forever).

2 Likes

Thanks for breaking it down!
I thought that I'm just missing the syntax here, but now I see the problem!

Let me reconsider the structure of my code. I think that I can work around this.

Thanks again!!

I took a guess and thought you may be trying for something like this:

trait MyTrait<'a> {
    fn some_fn<'b>(&'a mut self, s: &'b String)
    where
        'b: 'a;
}

struct MyStruct<'a>(Option<&'a String>);

impl<'a> MyTrait<'a> for MyStruct<'a> {
    fn some_fn<'b>(&'a mut self, s: &'b String)
    where
        'b: 'a,
    {
        self.0 = Some(s);
    }
}

let x = "x".to_string();
let y = "y".to_string();
let mut s = MyStruct(Some(&x));
s.some_fn(&y);

Thanks!
That would require me to add a lifetime to the trait itself. Which I was hoping to avoid.

If you want the trait implementations to be able to take advantage of lifetimes in the trait signature being long enough to match lifetimes in the Self type, then you must have a lifetime parameter in the trait. Otherwise, there's no way to express "this impl is only usable when the lifetime that appears in the trait method matches this lifetime in the type".

1 Like

Thanks @quinedot @jumpnbrownweasel @kpreid for your insight and explanation!

I refactored the code and got rid of the lifetimes completely.

Thanks for explaining so well what's happening. it would have taken me hours if not days to understand it on my own.
Thanks again!

1 Like