Where do I put my lifetime parameter?

I’m very new.

So I have a pair of trait methods that both return iterators over u32, but different kinds of iterators. I want the user to see both as basically one kind of iterator. I have this:

pub trait MyTrait {
    type MyIter :  Iterator<Item = u_nf>;
   // stuff
}

pub enum BasicIter<'a> {
    Range(Range<u_nf>),
    Slice(Iter<'a, u_nf>)
}

impl<'a> Iterator for BasicIter<'a> {
    type Item = u_nf;
    fn next(&mut self) -> Option<u_nf> {
        match self {
            BasicIter:Range(iter) => iter.next(), // colons munged 
            BasicIter:Slice(iter) => iter.next()
        }
    }
}

I had to add the 'a to my impl because the slice iter (std::slice::Iter) needs it.

When I try to implement my trait, I do this:

impl<'a, T> MyTrait for MyType<T> {
    type NI = BasicIter<'a>;
   // stuff
}

The compiler complains:

error[E0207]: the lifetime parameter 'a is not constrained by the impl trait, self type, or predicates

It doesn’t like the lifetime parameter being attached to “impl”.

If I try this:

impl&lt;A&gt; MyTrait for MyType&lt;A&gt; {
    type NI = BasicIter<'a>;
}

The compiler complains:

error[E0261]: use of undeclared lifetime name 'a
   --> src/networks.rs:192:28
    |
192 |     type NI = BasicIter&lt;'a&gt;
    |                            ^^ undeclared lifetime

If I leave it off:

impl&lt;A&gt; MyTrait for MyType&lt;A&gt; {
    type NI = BasicIter;
error[E0106]: missing lifetime specifier
    |
192 |     type NI = BasicIter;
    |               ^^^^^^^^^^^^ expected lifetime parameter

Apparently there is not combination that will please the compiler. Help.

Sorry about the formatting. This forum software is … awkward.

Surround your code snippets with 3 backticks to get proper formatting. Also, it’s much easier for people to help if you put a minimal example of your code in the playground: https://play.rust-lang.org/

With that out of the way, it’s hard to say for sure how you should define your types because you’ve not shown enough code (e.g. what does MyType look like and how will it return the BasicIter type?).

You may need to add a generic lifetime parameter to MyTrait so it would look like:

pub trait MyTrait<'a> {
   type MyIter: Iterator<Item = u_nf>;

   fn iter(&'a self) -> Self::MyIter;
}

impl<'a, T> MyTrait<'a> for MyType {
    type MyIter = BasicIter<'a>; // Why is this associated type called NI in your example?

    // Then presumably something like this, where BasicIter could borrow from `self`
    fn iter(&'a self) -> Self::MyIter { ...  }
}

If you show more code, maybe we’ll come up with something (if the above doesn’t help).

Actually that helped a lot!

I’m past that error, on to a new one.

Thanks :slight_smile:

I’ve got it working now, with much screaming!

I guess I don’t have a good mental model about what happens to lifetimes when mixed into trait definitions and impl<…> clauses.

I understand lifetimes for function definitions. That says, “When I call this function with these parameters, make sure the references I return last at least as long as these things I provided.”

That’s easy to understand.

Something like this:

pub trait BackingItems<'a> {
    type A : 'a + Item;
    type ItemIterator : Iterator<Item=&'a Self::A>;

    fn underlying_items(&'a self) -> Self::ArcIterator;
}

impl<'a, A : Arc> BackingItems<'a> for BasicObject<A> where A : 'a {
    type A = A;
    type ItemIter = slice::Iter<'a, A>;

    fn underlying_items(&'a self) -> Self::ItemIter {
        self.items.iter()
    }

I don’t see how that lifetime ('a) relates to the actually call flow of my code.

I get putting the 'a into the Iterator<Item=&'A>. I also get putting it into the parameter of underlying_times. That’s just the normal case: function call and returned type. But why do I need the where A : 'a clause?

It’s weird.

Since you’re indicating that you have &'a A yielded by the ItemIterator, the compiler wants you to explicitly indicate that A is going to be valid for that lifetime 'a. This ensures that the reference doesn’t outlive the referenced content.

I believe this constraint is going to be unnecessary in Rust 2018 (ie it’ll be implied in cases like this and doesn’t need to be stated manually).

1 Like

Yeah it’s starting to sink in. Thanks!