sabsad
sabsad

Reputation: 23

How do I specify the lifetime for the associated type of an iterator that refers to itself but does not mutate itself?

I have this struct:

struct RepIter<T> {
    item: T
}

I want to implement Iterator for it so that it returns a reference to its item every time:

impl<T> Iterator for RepIter<T> {
    type Item = &T;
    fn next(&mut self) -> Option<Self::Item> {
        return Some(&self.item);
    }
}

This doesn't compile since a lifetime must be specified for type Item = &T;. Searching for a way to do this I found this question. The first solution doesn't seem applicable since I'm implementing a preexisting trait. Trying to copy the second solution directly I get something like this:

impl<'a, T> Iterator for &'a RepIter<T> {
    type Item = &'a T;
    fn next(self) -> Option<&'a T> {
        return Some(&self.item);
    }
}

This doesn't work either since I need a mutable self as argument to next. The only way I was able to get it to compile was to write it like this:

impl<'a, T> Iterator for &'a RepIter<T> {
    type Item = &'a T;
    fn next(&mut self) -> Option<&'a T> {
        return Some(&self.item);
    }
}

But now self is a reference to a reference, right? I don't know how to call next on an instance of RepIter. For example, this doesn't work:

fn main() {
    let mut iter: RepIter<u64> = RepIter { item: 5 };
    let res = iter.next();
}

This makes me think my implementation of the trait could be written in a better way.

Upvotes: 2

Views: 1259

Answers (3)

Francis Gagn&#233;
Francis Gagn&#233;

Reputation: 65692

When designing an iterator, it's often useful to have distinct types for the collection and for the iterator over that collection. Usually, the collection will own the data, and the iterator will borrow from the collection. Collection types typically implement IntoIterator and don't implement Iterator. This means that creating an iterator happens in two steps: we need to create the collection first, then create the iterator from the collection.

Here's a solution that turns your RepIter type into a collection. I'll use Shepmaster's proposition to use iter::repeat to produce the iterator.

use std::iter::{self, Repeat};

struct RepIter<T> {
    item: T,
}

impl<T> RepIter<T> {
    // When IntoIterator is implemented on `&Self`,
    // then by convention, an inherent iter() method is provided as well.
    fn iter(&self) -> Repeat<&T> {
        iter::repeat(&self.item)
    }
}

impl<'a, T> IntoIterator for &'a RepIter<T> {
    type Item = &'a T;
    type IntoIter = Repeat<&'a T>;

    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}

fn main() {
    let iter: RepIter<u64> = RepIter { item: 5 };

    let res = iter.iter().next();
    println!("{:?}", res);

    let res = iter.iter().fuse().next();
    println!("{:?}", res);

    let res = iter.iter().by_ref().next();
    println!("{:?}", res);
}

Upvotes: 3

Peter Hall
Peter Hall

Reputation: 58715

As discussed in the question that Shepmaster linked to, this is a bit tricky because you really want to change the type of next(), but you can't because it's part of the trait. There are a couple of approaches to solve this though.

Making minimal changes to your code, you can just use the Iterator implementation on the &'a RepIter<T>:

pub fn main() {
    let mut iter = RepIter { item: 5 };
    let res = (&iter).next();
}

It's a bit unpleasant though.

Another way of looking at this is to change the ownership of your item. If it was already borrowed, then you can make all the types match up nicely:

struct RepIter<'a, T: 'a> {
    item: &'a T,
}

impl<'a, T> Iterator for RepIter<'a, T> {
    type Item = &'a T;
    fn next(&mut self) -> Option<&'a T> {
        Some(&self.item)
    }
}

pub fn main() {
    let val: u64 = 5;
    let mut iter = RepIter { item: &val };
    let res = iter.next();
}

Upvotes: 3

Shepmaster
Shepmaster

Reputation: 430544

I would recommend writing your code as:

use std::iter;

fn main() {
    let val = 5u64;
    let mut iter = iter::repeat(&val);
    let res = iter.next();
}

One thing that I don't quite understand yet is that your existing code almost works, but only for certain Iterator methods; those that take self by value instead of reference!

struct RepIter<T> {
    item: T,
}

impl<'a, T> Iterator for &'a RepIter<T> {
    type Item = &'a T;
    fn next(&mut self) -> Option<&'a T> {
        return Some(&self.item);
    }
}

fn main() {
    let iter: RepIter<u64> = RepIter { item: 5 };

    // Works
    let res = iter.fuse().next();
    println!("{:?}", res);

    // Doesn't work
    let res = iter.by_ref().next();
    println!("{:?}", res);
}

There's probably some interesting interaction happening.

Upvotes: 2

Related Questions