Iterating short-lived objects


#1

The current implementation of Iterator requires that objects returned from next() remain valid for the lifetime of the iterator, which works great when you have a collection of objects to iterate over. Unfortunately it does not work when you have a collection of “views” of an object which only remain valid for a short period of time. For example:

#[deriving(Show)]
struct View<'a, 'b> {
    _landscape: &'b mut Landscape<'a>
}
#[deriving(Show)]
struct Landscape<'a>;

impl<'a> Landscape<'a> {
    fn iter<'b>(&'a mut self) -> LandscapeIterator<'b, 'a> {
        LandscapeIterator {
            landscape: self
        }
    }
}

struct LandscapeIterator<'a, 'b> {
    landscape: &'b mut Landscape<'a>,
}

impl<'a, 'b> LandscapeIterator<'a, 'b> {
    fn next<'c>(&'c mut self) -> Option<View<'a, 'c>> {
        Some(View { _landscape: self.landscape })
    }
}
fn main() {
    let mut landscape = Landscape;
    let mut iter = landscape.iter();
    loop {
        let view = iter.next();
        println!("view: {}", view);
        break;
    }
}

In this example, we want to iterate over a number of different views of a landscape - only one view is valid at a time. Here it is not possible to implement Iterator, since the definition of next() does not match the definition in Iterator due to the lifetime parameter, and Iterator<View<'a, 'c>> could not be used in the definition since the lifetime would not be in scope. Since Iterator isn’t implemented, a for loop cannot be used (at least, it can’t be since https://github.com/rust-lang/rust/commit/caa564bea3d5f5a24d0797c4769184c1ea0abaff).

I encountered this scenario while writing some low-level networking code, where each call to next() would either read into a buffer, then return a slice of the buffer; or if the buffer already contains packets, simply return a slice. It would seem there is currently no nice way to provide an Iterator for such a stream of packets, and instead a loop like the one above must be used.

I don’t currently have a proposal to support Iterator for short-lived objects, but would be interested to know if there are any clever tricks which could enable this.


#2

An iterator that mutates and shares from its own state seems to me to be some kind of Zipper (a mutable Zipper in that case, but still in some way similar to the Haskell Zipper idea).

Maybe a separate little API could be formed around generic methods for “Zippers”.

A buffered line iterator, yielding a string slice from the buffer per line, is another quite common example of the same incompatible iterator.


#3

I also ran across this issue while writing a CSV parser and would love to have iterator support for it. But it presents problems. For example, I think the collect method would be invalid because only one “view” can exist at a time.

Perhaps @blake is on the right track here…