Reputation: 3517
Furthering the example of implementing IntoIterator
for a wrapped vector as per the Rust book, I am also trying to implement IntoIterator for a reference to the wrapper, as per the following code (Playground link):
struct VecWrapper(Vec<i32>);
impl VecWrapper {
fn iter(&'static self) -> Iter {
Iter(Box::new(self.0.iter()))
}
}
struct Iter(Box<Iterator<Item = &'static i32>>);
impl Iterator for Iter {
type Item = &'static i32;
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
impl IntoIterator for &'static VecWrapper {
type Item = &'static i32;
type IntoIter = Iter;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
fn main() {
// let test = vec![1, 2, 3]; // obviously, works
let test = VecWrapper(vec![1, 2, 3]); // not working
for v in &test {
println!("{}", v);
}
}
Although the implementation compiles, the attempt to use it in main
doesn't with the following error:
error[E0597]: `test` does not live long enough
--> src/main.rs:31:14
|
31 | for v in &test {
| ^^^^^
| |
| borrowed value does not live long enough
| argument requires that `test` is borrowed for `'static`
...
34 | }
| - `test` dropped here while still borrowed
This code is greatly simplified from what I would actually want to use as to using only 'static
lifetimes, using an existing contained type, and using i32
for the inner (iterated) type, but it is boiled down to show just the problem.
The accepted answer solves the first part of the problem as to not using 'static
and using + 'a
with traits. I still am having a problem with the actual code, which is a LazyList
implementation. I've posted that as Am I incorrectly implementing IntoIterator for a reference to a LazyList implementation or is this a Rust bug?.
Upvotes: 4
Views: 3599
Reputation: 430663
You have correctly implemented the iterator for references to VecWrapper
s that live for the entire length of the program — the 'static
lifetime.
Chances are you want to have a generic lifetime. That lifetime will then be provided a concrete lifetime, unique to each instantiation. Usually, we are lazy and just give this lifetime the name 'a
:
struct VecWrapper(Vec<i32>);
impl VecWrapper {
fn iter(&self) -> Iter {
Iter(Box::new(self.0.iter()))
}
}
struct Iter<'a>(Box<dyn Iterator<Item = &'a i32> + 'a>);
impl<'a> Iterator for Iter<'a> {
type Item = &'a i32;
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
impl<'a> IntoIterator for &'a VecWrapper {
type Item = &'a i32;
type IntoIter = Iter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
fn main() {
let test = VecWrapper(vec![1, 2, 3]);
for v in &test {
println!("{}", v);
}
}
Important changes:
Box<dyn Iterator<Item = &'a i32> + 'a>
- + 'a
was added. This is needed because a trait object will assume that there are no interior values that reference anything with a short lifetime.Item
type is now &'a i32
.<'a>
).See also:
Normally, there wouldn't be a reason to use a trait object here. I'd just embed the iterator directly:
struct Iter<'a>(std::slice::Iter<'a, i32>);
This avoids the need to have any indirection, which is unused in this case anyway. Additionally, it more obviously couples the lifetimes.
Upvotes: 14