I am having trouble expressing the lifetime of the return value of an Iterator
implementation. How can I compile this code without changing the return value of the iterator? I'd like it to return a vector of references.
It is obvious that I am not using the lifetime parameter correctly but after trying various ways I just gave up, I have no idea what to do with it.
use std::iter::Iterator;
struct PermutationIterator<T> {
vs: Vec<Vec<T>>,
is: Vec<usize>,
}
impl<T> PermutationIterator<T> {
fn new() -> PermutationIterator<T> {
PermutationIterator {
vs: vec![],
is: vec![],
}
}
fn add(&mut self, v: Vec<T>) {
self.vs.push(v);
self.is.push(0);
}
}
impl<T> Iterator for PermutationIterator<T> {
type Item = Vec<&'a T>;
fn next(&mut self) -> Option<Vec<&T>> {
'outer: loop {
for i in 0..self.vs.len() {
if self.is[i] >= self.vs[i].len() {
if i == 0 {
return None; // we are done
}
self.is[i] = 0;
self.is[i - 1] += 1;
continue 'outer;
}
}
let mut result = vec![];
for i in 0..self.vs.len() {
let index = self.is[i];
result.push(self.vs[i].get(index).unwrap());
}
*self.is.last_mut().unwrap() += 1;
return Some(result);
}
}
}
fn main() {
let v1: Vec<_> = (1..3).collect();
let v2: Vec<_> = (3..5).collect();
let v3: Vec<_> = (1..6).collect();
let mut i = PermutationIterator::new();
i.add(v1);
i.add(v2);
i.add(v3);
loop {
match i.next() {
Some(v) => {
println!("{:?}", v);
}
None => {
break;
}
}
}
}
error[E0261]: use of undeclared lifetime name `'a`
--> src/main.rs:23:22
|
23 | type Item = Vec<&'a T>;
| ^^ undeclared lifetime
As far as I understand, you want want the iterator to return a vector of references into itself, right? Unfortunately, it is not possible in Rust.
This is the trimmed down
Iterator
trait:Note that there is no lifetime connection between
&mut self
andOption<Item>
. This means thatnext()
method can't return references into the iterator itself. You just can't express a lifetime of the returned references. This is basically the reason that you couldn't find a way to specify the correct lifetime - it would've looked like this:except that this is not a valid
next()
method forIterator
trait.Such iterators (the ones which can return references into themselves) are called streaming iterators. You can find more here, here and here, if you want.
Update. However, you can return a reference to some other structure from your iterator - that's how most of collection iterators work. It could look like this:
Note how lifetime
'a
is now declared onimpl
block. It is OK to do so (required, in fact) because you need to specify the lifetime parameter on the structure. Then you can use the same'a
both inItem
and innext()
return type. Again, that's how most of collection iterators work.As mentioned in other answers, this is called a streaming iterator and it requires different guarantees from Rust's
Iterator
. One crate that provides such functionality is aptly called streaming-iterator and it provides theStreamingIterator
trait.Here is one example of implementing the trait:
Unfortunately, streaming iterators will be limited until generic associated types (GATs) from RFC 1598 are implemented.
@VladimirMatveev's answer is correct in how it explains why your code cannot compile. In a nutshell, it says that an Iterator cannot yield borrowed values from within itself.
However, it can yield borrowed values from something else. This is what is achieved with
Vec
andIter
: theVec
owns the values, and the theIter
is just a wrapper able to yield references within theVec
.Here is a design which achieves what you want. The iterator is, like with
Vec
andIter
, just a wrapper over other containers who actually own the values.(Playground)
Unrelated to your initial problem. If this were just me, I would ensure that all borrowed vectors are taken at once. The idea is to remove the repeated calls to
add
and to pass directly all borrowed vectors at construction:(Playground)
(EDIT: Changed the iterator design to take a
Vec<&'a [T]>
rather than aVec<Vec<&'a T>>
. It's easier to take a ref to container than to build a container of refs.)