Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When &self has different lifetime than the struct

Tags:

rust

lifetime

On this example

use std::marker::PhantomData;

pub struct A<'a, T> {
    elements: Vec<B<'a, T>>
}

pub struct B<'a, T> {
    _phantom: PhantomData<&'a T>
}

impl<'a, T> A<'a, T> {
    pub fn iter(& self) -> Iter<'a, T> {
        Iter {
            iter: self.elements.iter(),
        }
    }
}

pub struct Iter<'a, T> {
    iter: std::slice::Iter<'a, B<'a, T>>,
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=e246ef19b9ae5f1d405bde7c59d456d7

I get

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
  --> src/lib.rs:14:24
   |
14 |             iter: self.elements.iter(),
   |                                 ^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime defined here...
  --> src/lib.rs:12:17
//...

I know why this happens: the elements in self.elements lives as long as &self, so it cannot possibly create an Iter with lifetime a. The easy solution would be to do

pub fn iter(&'a self) -> Iter<'a, T> {
    Iter {
        iter: self.elements.iter(),
    }
}

but then I'm forced to borrow the &self for its entire existence which leads me to other problems. Whatis the easiest solution here?

like image 799
Rafaelo Avatar asked Jan 24 '23 04:01

Rafaelo


1 Answers

Your Iter implementation is over-constrained; you have two unrelated lifetimes that are required to be the same. You should separate them:

impl<'a, T> A<'a, T> {
    pub fn iter(&self) -> Iter<'a, '_, T> {
        Iter {
            iter: self.elements.iter(),
        }
    }
}

pub struct Iter<'a, 'b, T> {
    iter: std::slice::Iter<'b, B<'a, T>>,
}

That way, even if 'a is invariant, you don't run into issues with linking that lifetime to self. See it working on the playground (with additional tests).

like image 129
kmdreko Avatar answered Apr 02 '23 01:04

kmdreko