Lifetimeception - Trait usage with lifetime in trait and impl

I need a lifetime parameter for the following trait because I want the actual type of the Iterator (e.g. Iter of slice contains has a lifetime parameter). It can't be a method lifetime parameter, because the return type depends on it.

pub trait Iterable<'a> {
    type Iter: Iterator;

    fn iterate(&'a self) -> Self::Iter;
}

To simplify things, I will use the following trait:

trait Test<'a> {}

I want to use this trait in a function like this function:

fn test<I>(t: I)
where
    for<'a> I: Test<'a>,
{
    {
        // First use of t
    }
    {
        // Second use of t
    }
}

So far so good, if I impl this trait for any normal type the function works as expected.
But if I try to use a type containing a reference, the compiler complains that it needs to borrow the value for 'static.
This is kinda unexpected since I don't even use the value nor return anything. The problem is obviously for<'a> but I see no other way to tell the compiler "I implements the trait for every lifetime that is within the function scope" since I can't restrict the lifetime 'a. Furthermore, I can't make 'a a lifetime parameter of test because this would mean I would be borrowed for 'a after the first use, which is longer than the scope of the function.

Minimal failing example would be

struct ContainingReference<'a>(&'a i32);

// Implement the trait for every lifetime that is at most as long as 'a
impl<'a: 'b, 'b> Test<'b> for ContainingReference<'a> {}

fn main() {
     let i = 0;
     test(ContainingReference(&i));
}
error[E0597]: `i` does not live long enough
    |
    |         t(ContainingReference(&i));
    |         ----------------------^^--
    |         |                     |
    |         |                     borrowed value does not live long enough
    |         argument requires that `i` is borrowed for `'static`
...
    |     }
    |     - `i` dropped here while still borrowed

Consider whether you actually want IntoIterator from the standard library. You can implement this for a reference type (&'a T) to replace your troublesome Iterable trait, and such implementations exist for all the usual collections.

3 Likes

If you really go as far as declaring your own replacement trait for IntoIterator, I'd at least recommend you to go all in for it to be worth it. I don't have much time, right now, so I'll just drop this here and come back later to this issue:

/// Conversion into an `Iterator`.
pub trait Iterable<'a> {
    /// The type of the elements being iterated over.
    type Element: 'a;

    /// Which kind of iterator are we turning this into?
    type Iterator: Iterator<Item = &'a Self::Element>;

    /// Creates an iterator from a reference.
    fn iterator(&'a self) -> Self::Iterator;
}

/// Conversion into an `Iterator`.
pub trait IterableMut<'a>: Iterable<'a> {
    /// Which kind of iterator are we turning this into?
    type IteratorMut: Iterator<Item = &'a mut Self::Element>;

    /// Creates an iterator from a mutable reference.
    fn iterator_mut(&'a mut self) -> Self::IteratorMut;
}

/// Conversion into an `Iterator`.
pub trait IterableOnce<'a>: IterableMut<'a> {
    /// Which kind of iterator are we turning this into?
    type IntoIterator: Iterator<Item = Self::Element>;

    /// Creates an iterator from a value.
    fn into_iterator(self) -> Self::IntoIterator;
}

Thanks for the reply, I did try this and it removed a lot of lifetime parameters from my code:

trait Test {}

fn test<I>(t: I)
where
   for<'a> &'a I: Test,
{
}

struct ContainingReference<'a>(&'a i32);

impl<'a, 'b: 'a> Test for &'a ContainingReference<'b> {}

Sadly, this has the same problem, I still can't name a type that satisfies the trait for free usage in a method (here test) because 'a = 'static follows from the code.

Sticking with IntoIterator, how do I name a type T that implements IntoIterator for &T inside a function?

I found an article with essentially the same problem, and this is impossible right now since GAT is still not a thing.

Sorry I couldn't be of more help! I hope you're able to find a workaround.

1 Like