A value that returns an iterator with a lifetime bounded by the lifetime of the argument

I have a trait that says that any implementation Foo

must provide a method bar

that returns an object of some type that implements Iterator<Item = u32>

:

trait Foo {
    type FooIterator: Iterator<Item = u32>;
    fn bar(&self) -> FooIterator;
}

      

In this case, I believe that the default lifelong exception means that the iterator returned bar

should live on its own, not tied to the lifetime Foo

, it iterates. Habnabit user on #rust irc suggested the following way of saying that lifetime is FooIterator

less than lifetime Foo

. that is, it allows the implementation FooIterator

to keep the reference to Foo

where it is derived from:

trait Foo<'a> {
    type FooIterator: Iterator<Item = u32> + 'a;
    fn bar<'b: 'a>(&'b self) -> Self::FooIterator;
}

      

What I really want is a case where the function bar

takes an extra argument and the implementation is FooIterator

allowed to keep a reference to both the Foo

extra argument. those. the lifetime is FooIterator

limited by the lifetime Foo

and the lifetime of the additional argument.

My literal translation of this idea would be

trait Zip {}
trait Foo<'a, 'c> {
    type FooIterator: Iterator<Item = u32> + 'a + 'c;
    // Foo.bar() returns an iterator that has a lifetime less than the Foo
    fn bar<'b: 'a, 'd: 'c>(&'b self, &'d Zip) -> Self::FooIterator;
}

      

But I was told that there is no "good" way to do it. What would be the best way to implement this idiom? What would the above code do?

+3


source to share


2 answers


What you are looking for is related type constructors , a planned feature that has yet to be implemented in Rust. With associated type constructors, your code would look like this:

trait Zip {}
trait Foo {
    type FooIterator<'a, 'c>: Iterator<Item = u32> + 'a + 'c;
    // Foo.bar() returns an iterator that has a lifetime less than the Foo
    fn bar<'a, 'b: 'a, 'c, 'd: 'c>(&'b self, &'d Zip) -> Self::FooIterator<'a, 'c>;
}

      



In fact, I'm not sure if all of these lifetimes are necessary because one &'a T

can be forced to &'b T

where 'a: 'b

. So the following might be good enough:

trait Zip {}
trait Foo {
    type FooIterator<'a, 'c>: Iterator<Item = u32> + 'a + 'c;
    // Foo.bar() returns an iterator that has a lifetime less than the Foo
    fn bar<'a, 'c>(&'a self, &'c Zip) -> Self::FooIterator<'a, 'c>;
}

      

+4


source


Depending on how you want to use this trait, you can make it work by implementing it for &'a Struct

instead Struct

, thereby "raising" the responsibility for finding the correct lifetime from that trait to the caller.

Remove the lifetime annotation from the trait and change bar

to take self

, plus another argument of the same lifetime:

trait Foo {
    type FooIterator: Iterator<Item = u32>;
    fn bar(self, other: Self) -> Self::FooIterator;
}

      

(Extracting 'a

from a trait is possible because it bar

consumes the link instead of re-markup - self

no longer need to survive the return value because it has been moved into it.)

Then impl

for the lifetime reference 'a

:



impl<'a> Foo for &'a Vec<u32> {
    type FooIterator = ...; // something presumably containing 'a
    fn bar(self, other: Self) -> Self::FooIterator {
        ...
    }
}

      

This works because the compiler can limit the lifetime of the 'a

one for which impl is applied.

Here's a link to playgrounds where it bar

is basically a wrapper around .chain()

.

I ignore the trait Zip

because how to include it depends on what it provides. Instead, I believe it bar

only takes an argument of the same type as self

. However, perhaps you can add it as well, perhaps using the same method if you need to.

+1


source







All Articles