Reputation: 1102
I have a trait with a function that returns Box<dyn Iterator<...>>
. As I understand it, based off the definition, the lifetime of this returned iterator needs to match the trait object's. But in a function that drops both at the end, I get a lifetime error. Here's a minimal example, with assumptions added as comments:
trait Foo<'a> {
// The returned iterator's lifetime should match self's
fn bar(&'a self) -> Box<dyn Iterator<Item = usize> + 'a>;
}
struct FooImpl {
values: Vec<usize>
}
impl<'a> Foo<'a> for FooImpl {
fn bar(&'a self) -> Box<dyn Iterator<Item = usize> + 'a> {
Box::new(self.values.iter().cloned())
}
}
fn foo_bar_caller<'a>(foo: Box<dyn Foo<'a>>) {
// `foo` is moved into this fn
let _iter = foo.bar();
// `_iter` and `foo` should drop
}
fn main() {
let foo = FooImpl { values: vec![1, 2, 3, 4, 5] };
foo_bar_caller(Box::new(foo));
}
(rust playground with the code.) Running this gives the following lifetime error:
error[E0597]: `*foo` does not live long enough
--> src/main.rs:17:17
|
16 | fn foo_bar_caller<'a>(foo: Box<dyn Foo<'a>>) {
| -- lifetime `'a` defined here
17 | let _iter = foo.bar();
| ^^^^^^^^^
| |
| borrowed value does not live long enough
| argument requires that `*foo` is borrowed for `'a`
18 | // `_iter` and `foo` should drop
19 | }
| - `*foo` dropped here while still borrowed
For more information about this error, try `rustc --explain E0597`.
error: could not compile `playground` due to previous error
Upvotes: 1
Views: 56
Reputation: 7579
You probably meant to make the function Foo::bar
generic over the reference lifetime, not the trait Foo
:
trait Foo {
// The returned iterator's lifetime should match self's
fn bar<'a>(&'a self) -> Box<dyn Iterator<Item = usize> + 'a>;
}
By putting the lifetime on the trait Foo
, you are unnecessarily (in this context, at least) restricting the lifetimes of the references you can use with a particular Foo::<'_>::bar
implementation. A <T as Foo<'a>>::bar
(T
in the context of foo_bar_caller
being Box<dyn Foo<'a>>
) call can only take references with the lifetime 'a
, but as @MaximilianBurszley points out in the comments, what you are actually passing to it is a temporary borrow that does not have the same lifetime 'a
.
Note that you can make foo_bar_caller
work with your existing code (with the trait Foo
being generic over the lifetime) if you make the function take a reference with the same lifetime:
fn foo_bar_caller<'a>(foo: &'a dyn Foo<'a>) {
// `foo` is moved into this fn
let _iter = foo.bar();
// `_iter` and `foo` should drop
}
fn main() {
let foo = FooImpl { values: vec![1, 2, 3, 4, 5] };
foo_bar_caller(&foo);
}
Alternatively, you can use a higher-ranked trait bound (HRTB) to specify that foo_bar_caller
must take in a value that implements Foo<'b>
for all lifetimes 'b
:
fn foo_bar_caller(foo: Box<dyn for<'b> Foo<'b>>) {
// `foo` is moved into this fn
let _iter = foo.bar();
// `_iter` and `foo` should drop
}
This allows the Foo<'_>
implementation for the value foo
to be valid for the lifetime for the temporary borrow created during the foo.bar()
call (since it implements Foo<'_>
for all lifetimes).
Upvotes: 3