Trait which returns iterator with lifetime bounded by the lifetime of an argument - rust

I have a trait which says that any implementation of Foo needs to provide a method bar which returns an object of some type which implements Iterator<Item = u32>:
trait Foo {
type FooIterator: Iterator<Item = u32>;
fn bar(&self) -> FooIterator;
}
For this case, I believe that the default lifetime elision means that the iterator returned by bar is required to live on its own, without being tied to the lifetime of the Foo it is iterating over. User Habnabit on #rust irc suggested the following way to say that the lifetime of the FooIterator is less than the lifetime of the Foo. i.e. it allows the implementation of the FooIterator to keep a reference to the Foo that it comes from:
trait Foo<'a> {
type FooIterator: Iterator<Item = u32> + 'a;
fn bar<'b: 'a>(&'b self) -> Self::FooIterator;
}
What I really want is the case where the function bar takes an additional argument, and the implementation of FooIterator is allowed to keep a reference to both the Foo and the additional argument. i.e. the lifetime of FooIterator is bounded by the lifetime of the 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 there there is no "good" way to do this. What would be the best way to implement this idiom? What would the above code do exactly?

What you're looking for is associated type constructors, a planned feature that is not yet 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>;
}
Actually, I'm not sure all those lifetimes are necessary, because a &'a T can be coerced to a &'b T where 'a: 'b. Thus, 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>;
}

Depending on how you want to use this trait, you may be able to make it work by implementing it for &'a Struct instead of for Struct, thus "hoisting" the responsibility for finding the right lifetime from the trait into the caller.
Remove the lifetime annotation from the trait and change bar so it takes self, plus another argument of the same lifetime:
trait Foo {
type FooIterator: Iterator<Item = u32>;
fn bar(self, other: Self) -> Self::FooIterator;
}
(Removing 'a from the trait is possible because bar consumes the reference instead of reborrowing it -- self doesn't have to outlive the return value anymore because it's been moved into it.)
Then impl it for a reference of lifetime '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 'a to one for which the impl applies.
Here's a playground link where bar is basically a wrapper around .chain().
I'm ignoring the Zip trait for now because how to incorporate it depends on what it provides. Instead, I suppose that bar only accepts an argument of the same type as Self. However, you can probably add it as well, maybe using the same technique if you need to.

Related

Lifetime elision for &mut self and output values of functions

I am trying to better understand lifetime elisions for &mut self and corresponding output parameters.
Let's say I have a function signature of the form
fn foo(&mut self) -> &mut bar
From the lifetime section in the Rustonomicon I understand that this gets expanded to
fn foo<'a>(&'a mut self) -> &'a mut bar
which has the side effect that self gets mutably borrowed as long as the corresponding object exists.
What I would like to know is what happens if I specify a function with signature
fn foo<'a>(&mut self) -> &'a mut bar
In my understanding, &mut self is elided and according to elision rules gets its own input lifetime. So we would end up with something like
fn foo<'b, 'a>(&'b mut self) -> &'a mut bar
According to elision rules, 'b gets assigned to all elided output lifetimes. But 'a is not elided. So what is the lifetime 'a? Code with such a signature compiles fine. But I don't quite understand the lifetime implications for 'a.
You've correctly un-elided the lifetimes.
So what is the lifetime 'a?
Whenever a function has a lifetime parameter, it is always: "whatever the caller wants it to be" (subject to bounds, which there aren't in this case).
Code with such a signature compiles fine.
Did you try writing a body for that function? Given those lifetimes, it can't actually do very much. For example,
impl Foo {
fn foo<'b, 'a>(&'b mut self) -> &'a mut Bar {
&mut self.bar
}
}
will fail to compile with errors telling you (indirectly) that this is only valid if 'b outlives 'a, which it doesn't.
If you add that "outlives" relation to the signature, then the code can compile:
impl Foo {
fn foo<'b, 'a>(&'b mut self) -> &'a mut Bar
where
'b: 'a
{
&mut self.bar
}
}
However, this is almost never any more useful than the simpler
fn foo<'a>(&'a mut self) -> &'a mut Bar {
because references can generally be used as if they have shorter lifetimes than their type specifies, so 'a here can serve the purpose of 'a and the purpose of 'b in the more complex declaration.
Like any other generic parameter, 'a can be freely chosen by the caller as long as it satisfies any generic bounds on the trait (which there aren't any of, in your example). Just as you have something like
// caller chooses the value of B in `fn collect<B>(self) -> B` to be Vec<_>
let items: Vec<_> = my_data.into_iter().collect();
you can have
// caller chooses the value of 'a in `fn foo<'a>(&mut self) -> &'a mut bar` to be 's
's: {
let b = obj.foo()
}
Of course, Rust won't even compile foo if it can't prove that 'a can indeed be freely chosen by the caller, which, in the absence of other lifetime bounds, means we must have 'a: 'static, and the body of foo must be compatible with that.

Difference between "Self" and elided lifetime

Don't know how to describe, but here is the minimal reproducing snippet (also on playground):
struct Ctx(Vec<Box<dyn Fn(&mut MockCtx)>>);
struct MockCtx<'a>(&'a mut Ctx);
impl MockCtx<'_> {
// this works
fn push<F: 'static + Fn(&mut MockCtx)>(&mut self, f: F) {
self.0.0.push(Box::new(f));
}
}
trait Push {
fn push<F: 'static + Fn(&mut Self)>(&mut self, f: F);
}
impl Push for MockCtx<'_> {
// fn push<F: 'static + Fn(&mut Self)>(&mut self, f: F) { (1)
fn push<F: 'static + Fn(&mut MockCtx)>(&mut self, f: F) { // (2)
MockCtx::push(self, f)
}
}
If I take (1), compiler reports
expected a `Fn<(&mut MockCtx<'_>,)>` closure, found `F`
and recommand to add explicit lifetime to restrict. If I do so or simply take (2), compiler reports
impl has stricter requirements than trait
The truth is I do not understand the problem from starting point... I understand what is lifetime and simple "outlive" rules, and to me compiler should have no worry about 'a for these code so far, because that can only be a thing when these Box<dyn Fn> actually get called.
Thanks for any replying and explaining!
This is understandably a little opaque, because several lifetimes are elided. It should become a little more clear if we write those out.
Let's first look at the container:
struct Ctx(Vec<Box<dyn Fn(&mut MockCtx)>>);
This is short for:
struct Ctx(Vec<Box<dyn for<'a, 'b> Fn(&'a mut MockCtx<'b>) + 'static>>);
The 'static requires that any closure you put in the container must own its data. It cannot refer to other data that depends on a lifetime. The elided lifetime is 'static because Box has no lifetime parameters.
The construct for<'a, 'b> Fn(&'a mut MockCtx<'b>) is a higher rank trait bound, or HRTB for short. This means that any closure you put in the container must be able to accept a mutable reference with any lifetime to a MockCtx<'_> with any lifetime parameter. Note for later that there are two lifetime parameters here.
Now, let's go to the impl:
impl MockCtx<'_> {
fn push<F: 'static + Fn(&mut MockCtx)>(&mut self, f: F);
}
This is short for:
impl MockCtx<'_> {
fn push<F: 'static + for<'a, 'b> Fn(&'a mut MockCtx<'b>)>(&mut self, f: F);
}
The requirements on the closure are the same as for the container. No conflicts. But note that the closure must accept types other than Self. It must accept a mutable reference to a MockCtx<'_> with a different lifetime.
Now let's go to the trait:
trait Push {
fn push<F: 'static + Fn(&mut Self)>(&mut self, f: F);
}
This is short for:
trait Push {
fn push<F: 'static + for<'a> Fn(&'a mut Self)>(&mut self, f: F);
}
This is going to conflict. A closure given to Push can only be called with a reference to Self, of any lifetime. But a closure that we store in our container must accept some types other than Self. There is a whole range of Self-like types that have different lifetime parameters that the closure needs to accept for our container.
Now, we can explain why the two attempts failed. #1 fails because the closure in the trait and the trait impl can only accept Self, but we're attempting to store it in a container that holds closures that can accept MockCtx<'_> of any lifetime. #2 fails because the trait only requires the closure accept Self, but our trait impl tries to accept a more restrictive set of closures that can accept MockCtx<'_> for any lifetime.
There are several ways to fix this, but they all amount to fixing this disagreement on the bounds for the argument of the closure.
One way is to explicitly accept the full range of types in the closure bound. You have to mention types other than Self. So, for example, you can mention the concrete types:
trait Push {
fn push<F: 'static + Fn(&mut MockCtx<'_>)>(&mut self, f: F);
}
See here.
Using a nightly feature of trait aliases, you could use a common name for all the requirements:
trait MockFn = 'static + Fn(&mut MockCtx<'_>);
trait Push {
fn push<F: MockFn>(&mut self, f: F);
}
See here.
Using a nightly feature of generic associated types, you could also make the trait slightly more general:
trait Push {
type SelfLike<'a>;
fn push<F: 'static + Fn(&mut Self::SelfLike<'_>)>(&mut self, f: F);
}
See here.
Alternatively, as #Jmb says in the comments, you can make the closure a generic parameter on the trait:
trait Push<F> {
fn push(&mut self, f: F);
}
See here.
This is a relatively clean approach. It is asserted in the comments that this is useless, but I don't think it is.

How to create an `Iterable` trait for references in Rust?

I'm trying to create a trait that captures the iter function in slice as well as VecDeque, BTreeMap and HashMap. I'd like the implementer of this trait to be able to specify and implement their own iterator type, but it looks like this iterator type must have a lifetime argument, and that cannot be given as an associated type.
In more detail, here's what I wish was possible in Rust:
trait RefIterable<T>
where for<'a> (T: 'a) => (Self::Iter<'a>: Iterator<Item = &'a T>)
{
type Iter; // Has kind (lifetime -> type)
fn refs<'a>(&'a self) -> Self::Iter<'a>
}
If this was possible, the implementation could look like this
impl RefIterable<T> for Vec<T> {
type Iter<'a> = std::slice::Iter<'a, T>; // This is not valid Rust code.
fn refs<'a>(&'a self) -> std::slice::Iter<'a, T> {
self.as_slice().iter()
}
}
I'm still relatively new to Rust, so I'm asking if there's already a way to do this that I'm not aware of, or if there's a nice workaround for this situation. I'd imagine that this situation is not very rare.
(Using Box<dyn 'a + Iterator<Item = &'a T>> is my current workaround, but that prevents some optimization from happening.)
Edit:
EvilTak's answer is probably the best thing we can do right now. The ability to combine all possible lifetimes together with the condition T: 'a into one unparametrized trait seems to be unsupported by Rust as of today.
Add the lifetime parameter to the trait instead, which allows you to use it in the associated type Iter's bound:
trait RefIterable<'a> {
type Item: 'a;
type Iter: Iterator<Item = &'a Self::Item>; // Has kind (lifetime -> type)
fn refs(&'a self) -> Self::Iter;
}
The Item: 'a bound is required to let the compiler know that the references (&'a Self::Item) do not outlive the type (Self::Item).
I have modified RefIterable to make it follow Iterator's convention of using an associated type to specify the type of the items that are iterated over for the same reason as the one behind Iterator's usage of an associated type.
Implementations are pretty straightforward:
impl<'a, T: 'a> RefIterable<'a> for Vec<T> {
type Item = T;
type Iter = std::slice::Iter<'a, T>;
fn refs(&'a self) -> std::slice::Iter<'a, T> {
self.as_slice().iter()
}
}
Playground

Why do the lifetimes on a trait object passed as an argument require Higher Ranked Trait Bounds but a struct doesn't?

How are lifetimes handled when there is a trait object passed to a function?
struct Planet<T> {
i: T,
}
trait Spinner<T> {
fn spin(&self, value: T);
}
impl<T> Spinner<T> for Planet<T> {
fn spin(&self, value: T) {}
}
// foo2 fails: Due to lifetime of local variable being less than 'a
fn foo2<'a>(t: &'a Spinner<&'a i32>) {
let x: i32 = 10;
t.spin(&x);
}
// foo1 passes: But here also the lifetime of local variable is less than 'a?
fn foo1<'a>(t: &'a Planet<&'a i32>) {
let x: i32 = 10;
t.spin(&x);
}
(Playground)
This code results in this error:
error[E0597]: `x` does not live long enough
--> src/main.rs:16:17
|
16 | t.spin(&x);
| ^ borrowed value does not live long enough
17 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 14:5...
--> src/main.rs:14:5
|
14 | fn foo2<'a>(t: &'a Spinner<&'a i32>) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The function signature of foo1 is nearly same as foo2. One receiving the reference to struct and the other a trait object.
I read this is where Higher Ranked Trait Bounds comes in. Modifying foo2 as foo2(t: &for<'a> Spinner<&'a i32>) compiles the code, but I don't understand why.
Why won't 'a shrink for x?
Citing the Nomicon:
How on earth are we supposed to express the lifetimes on F's trait bound? We need to provide some lifetime there, but the lifetime we care about can't be named until we enter the body of call! Also, that isn't some fixed lifetime; call works with any lifetime &self happens to have at that point.
Can this please be elaborated?
In short: foo1 compiles because most types are variant over their generic parameters and the compiler can still chose a Spinner impl for t. foo2 doesn't compile because traits are invariant over their generic parameters and the Spinner impl is already fixed.
Some explanation
Let's take a look at a third version of foo:
fn foo3<'a>(t: &'a Planet<&'a i32>) {
let x: i32 = 10;
Spinner::<&'a i32>::spin(t, &x);
}
This results in the same error as your foo2. What's going in there?
By writing Spinner::<&'a i32>::spin, we force the compiler to use a specific implementation of the Spinner trait. And the signature of Spinner::<&'a i32>::spin is fn spin(&self, value: &'a i32). Period. The lifetime 'a is given by the caller; foo can't choose it. Thus we have to pass a reference that lives for at least 'a. That's why the compiler error happens.
So why does foo1 compile? As a reminder:
fn foo1<'a>(t: &'a Planet<&'a i32>) {
let x: i32 = 10;
t.spin(&x);
}
Here, the lifetime 'a is also given by the caller and cannot be chosen by foo1. But, foo1 can chose which impl of Spinner to use! Note that impl<T> Spinner<T> for Planet<T> basically defines infinitely many specific implementations (one for each T). So the compiler also knows that Planet<&'x i32> does implement Spinner<&'x i32> (where 'x is the specific lifetime of x in the function)!
Now the compiler just has to figure out if it can turn Planet<&'a i32> into Planet<&'x i32>. And yes, it can, because most types are variant over their generic parameters and thus Planet<&'a i32> is a subtype of Planet<&'x i32> if 'a is a subtype of 'x (which it is). So the compiler just "converts" t to Planet<&'x i32> and then the Spinner<&'x i32> impl can be used.
Fantastic! But now to the main part: why doesn't foo2 compile then? Again, as a reminder:
fn foo2<'a>(t: &'a Spinner<&'a i32>) {
let x: i32 = 10;
t.spin(&x);
}
Again, 'a is given by the caller and foo2 cannot chose it. Unfortunately, now we already have a specific implementation! Namely Spinner<&'a i32>. We can't just assume that the thing we were passed also implements Spinner<&'o i32> for any other lifetime 'o != 'a! Traits are invariant over their generic parameters.
In other words: we know we have something that can handle references which live at least as long as 'a. But we can't assume that the thing we were handed can also handle lifetimes shorter than 'a!
As an example:
struct Star;
impl Spinner<&'static i32> for Star {
fn spin(&self, value: &'static i32) {}
}
static SUN: Star = Star;
foo2(&SUN);
In this example, 'a of foo2 is 'static. And in fact, Star implements Spinner only for 'static references to i32.
By the way: this is not specific to trait objects! Let's look at this fourth version of foo:
fn foo4<'a, S: Spinner<&'a i32>>(t: &'a S) {
let x: i32 = 10;
t.spin(&x);
}
Same error once again. The problem is, again, that the Spinner impl is already fixed! As with the trait object, we only know that S implements Spinner<&'a i32>, not necessarily more.
HRTB to the rescue?
Using higher ranked trait bounds resolves the issue:
fn foo2(t: &for<'a> Spinner<&'a i32>)
and
fn foo4<S: for<'a> Spinner<&'a i32>>(t: &S)
As it's hopefully clear from the explanation above, this works because we the specific impl of Spinner isn't fixed anymore! Instead, we again have infinitely many impls to choose from (one for each 'a). Thus we can choose the impl where 'a == 'x.

How do I specify lifetime parameters in an associated type?

I have this trait and simple structure:
use std::path::{Path, PathBuf};
trait Foo {
type Item: AsRef<Path>;
type Iter: Iterator<Item = Self::Item>;
fn get(&self) -> Self::Iter;
}
struct Bar {
v: Vec<PathBuf>,
}
I would like to implement the Foo trait for Bar:
impl Foo for Bar {
type Item = PathBuf;
type Iter = std::slice::Iter<PathBuf>;
fn get(&self) -> Self::Iter {
self.v.iter()
}
}
However I'm getting this error:
error[E0106]: missing lifetime specifier
--> src/main.rs:16:17
|
16 | type Iter = std::slice::Iter<PathBuf>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected lifetime parameter
I found no way to specify lifetimes inside that associated type. In particular I want to express that the iterator cannot outlive the self lifetime.
How do I have to modify the Foo trait, or the Bar trait implementation, to make this work?
Rust playground
There are a two solutions to your problem. Let's start with the simplest one:
Add a lifetime to your trait
trait Foo<'a> {
type Item: AsRef<Path>;
type Iter: Iterator<Item = Self::Item>;
fn get(&'a self) -> Self::Iter;
}
This requires you to annotate the lifetime everywhere you use the trait. When you implement the trait, you need to do a generic implementation:
impl<'a> Foo<'a> for Bar {
type Item = &'a PathBuf;
type Iter = std::slice::Iter<'a, PathBuf>;
fn get(&'a self) -> Self::Iter {
self.v.iter()
}
}
When you require the trait for a generic argument, you also need to make sure that any references to your trait object have the same lifetime:
fn fooget<'a, T: Foo<'a>>(foo: &'a T) {}
Implement the trait for a reference to your type
Instead of implementing the trait for your type, implement it for a reference to your type. The trait never needs to know anything about lifetimes this way.
The trait function then must take its argument by value. In your case you will implement the trait for a reference:
trait Foo {
type Item: AsRef<Path>;
type Iter: Iterator<Item = Self::Item>;
fn get(self) -> Self::Iter;
}
impl<'a> Foo for &'a Bar {
type Item = &'a PathBuf;
type Iter = std::slice::Iter<'a, PathBuf>;
fn get(self) -> Self::Iter {
self.v.iter()
}
}
Your fooget function now simply becomes
fn fooget<T: Foo>(foo: T) {}
The problem with this is that the fooget function doesn't know T is in reality a &Bar. When you call the get function, you are actually moving out of the foo variable. You don't move out of the object, you just move the reference. If your fooget function tries to call get twice, the function won't compile.
If you want your fooget function to only accept arguments where the Foo trait is implemented for references, you need to explicitly state this bound:
fn fooget_twice<'a, T>(foo: &'a T)
where
&'a T: Foo,
{}
The where clause makes sure that you only call this function for references where Foo was implemented for the reference instead of the type. It may also be implemented for both.
Technically, the compiler could automatically infer the lifetime in fooget_twice so you could write it as
fn fooget_twice<T>(foo: &T)
where
&T: Foo,
{}
but it's not smart enough yet.
For more complicated cases, you can use a Rust feature which is not yet implemented: Generic Associated Types (GATs). Work for that is being tracked in issue 44265.
Use a wrapper type
If the trait and all its implementations are defined in one crate, a helper type can be useful:
trait Foo {
fn get<'a>(&'a self) -> IterableFoo<'a, Self> {
IterableFoo(self)
}
}
struct IterableFoo<'a, T: ?Sized + Foo>(pub &'a T);
For a concrete type that implements Foo, implement the iterator conversion on the IterableFoo wrapping it:
impl Foo for Bar {}
impl<'a> IntoIterator for IterableFoo<'a, Bar> {
type Item = &'a PathBuf;
type IntoIter = std::slice::Iter<'a, PathBuf>;
fn into_iter(self) -> Self::IntoIter {
self.0.v.iter()
}
}
This solution does not allow implementations in a different crate. Another disadvantage is that an IntoIterator bound cannot be encoded into the definition of the trait, so it will need to be specified as an additional (and higher-rank) bound for generic code that wants to iterate over the result of Foo::get:
fn use_foo_get<T>(foo: &T)
where
T: Foo,
for<'a> IterableFoo<'a, T>: IntoIterator,
for<'a> <IterableFoo<'a, T> as IntoIterator>::Item: AsRef<Path>
{
for p in foo.get() {
println!("{}", p.as_ref().to_string_lossy());
}
}
Associated type for an internal object providing desired functionality
The trait can define an associated type that gives access to a part of the object that, bound in a reference, provides the necessary access traits.
trait Foo {
type Iterable: ?Sized;
fn get(&self) -> &Self::Iterable;
}
This requires that any implementation type contains a part that can be so exposed:
impl Foo for Bar {
type Iterable = [PathBuf];
fn get(&self) -> &Self::Iterable {
&self.v
}
}
Put bounds on the reference to the associated type in generic code that uses the the result of get:
fn use_foo_get<'a, T>(foo: &'a T)
where
T: Foo,
&'a T::Iterable: IntoIterator,
<&'a T::Iterable as IntoIterator>::Item: AsRef<Path>
{
for p in foo.get() {
println!("{}", p.as_ref().to_string_lossy());
}
}
This solution permits implementations outside of the trait definition crate.
The bound work at generic use sites is as annoying as with the previous solution.
An implementing type may need an internal shell struct with the only purpose of providing the associated type, in case when the use-site bounds are not as readily satisfied as with Vec and IntoIterator in the example discussed.
In future, you'll want an associated type constructor for your lifetime 'a but Rust does not support that yet. See RFC 1598

Resources