Reputation: 592
I want to implement the Iterator
trait for a struct which contains an iterable field. Iterating over my struct should yield the same results obtained iterating over the field. This is what I'd like to have (obviously not working):
struct Foo {
bar: Vec<char>,
}
impl Iterator for Foo {
type Item: &char; // Error: expected named lifetime parameter
fn next(&mut self) -> Option<Self::Item> {
self.bar.iter().next()
}
}
To avoid that error I tried to insert lifetimes:
use std::marker::PhantomData;
struct Foo<'a> {
bar: Vec<char>,
phantom: &'a PhantomData<char> // not sure what to put inside < .. >
}
impl<'a> Iterator for Foo<'a> {
type Item = &'a char;
fn next(&mut self) -> Option<Self::Item> {
self.bar.iter().next() // again, several errors about lifetimes
}
}
How do I implement the Iterator
trait for such a struct?
Upvotes: 19
Views: 8078
Reputation: 1
I have #[readonly::make] on my structs. This will not cause a problem with readonly, and seems easy:
use std::slice::Iter;
pub fn iter(&self) -> Iter<Foo> {
self.bar.iter()
}
It may violate something, but I have used it in simple code without a problem.
Upvotes: 0
Reputation: 738
Here is a cheeky implementation by moi. Just because i'm new to rust :)
fn main() {
let stream = ScoreStream::new(vec![32, 21, 37]);
let i = stream.into_iter();
for a in i {
print!("\n {}", a);
}
}
struct ScoreStream {
vals: Vec<i32>,
count: usize,
}
impl ScoreStream {
fn new(vals: Vec<i32>) -> Self {
Self { vals, count: 0 }
}
}
impl Iterator for ScoreStream {
type Item = i32;
fn next(&mut self) -> Option<Self::Item> {
let res = self.vals.get(self.count);
match res {
Some(v) => {
self.count += 1;
Some(*v)
},
None => None,
}
}
}
Upvotes: 0
Reputation: 8980
There is a big difference between something that creates an Iterator
and something which is an Iterator
. For example, Vec<char>
can produce an iterator, but is not an iterator itself. Here are a couple simple examples of both so you can hopefully find something that fits your use case.
The easiest approach for your situation is to just implement Deref
for the field and let Vec<char>
handle it. Alternatively you could write a wrapper function for bar.iter()
.
pub struct Foo {
bar: Vec<char>,
}
impl Deref for Foo {
type Target = Vec<char>;
fn deref(&self) -> &Self::Target {
&self.bar
}
}
let foo = Foo { bar: vec!['a', 'b', 'c', 'd'] };
// deref is implicitly called so foo.iter() represents foo.bar.iter()
for x in foo.iter() {
println!("{:?}", x);
}
Here is how you can write your own iterator for a Vec<char>
. Note how the Vec
stores as a reference instead of an owned value. This is so rust can resolve the lifetime constraints. By holding an immutable reference for the lifetime of the iterator, we are guaranteeing that the references produced by this iterator can also persist for that lifespan. If we used an owned value, we can only guarantee that the lifetime of an element reference lasts until the next time a mutable reference is taken to the iterator. Or in other words, each value can only last until next
is called again. However even this requires nightly features to properly express.
pub struct SimpleIter<'a> {
values: &'a Vec<char>,
index: usize,
}
impl<'a> Iterator for SimpleIter<'a> {
type Item = &'a char;
fn next(&mut self) -> Option<Self::Item> {
if self.index >= self.values.len() {
return None
}
self.index += 1;
Some(&self.values[self.index - 1])
}
}
Here is a simple example of a generic iterator wrapping another iterator.
// Add ?Sized so Foo can hold a dynamically sized type to satisfy IntoFoo
struct Foo<I: ?Sized> {
bar: I,
}
impl<I: Iterator> Iterator for Foo<I> {
type Item = <I as Iterator>::Item;
fn next(&mut self) -> Option<Self::Item> {
println!("Iterating through Foo");
self.bar.next()
}
}
You can also get a bit more fancy with it by making a trait for easy usage of Foo
.
pub trait IntoFoo {
fn iter_foo(self) -> Foo<Self>;
}
// Add an iter_foo() method for all existing iterators
impl<T: Iterator> IntoFoo for T {
fn iter_foo(self) -> Foo<Self> {
Foo { bar: self }
}
}
let values = vec!['a', 'b', 'c', 'd'];
// Get default iterator and wrap it with our foo iterator
let foo: Foo<std::slice::Iter<'_, char>> = values.iter().iter_foo();
for x in foo {
println!("{:?}", x);
}
Upvotes: 32
Reputation: 994
An iterator is a stateful object which remembers its position in the underlying collection. In your example, each call to next()
would restart iterating from the start and return the first element (provided the errors were fixed).
Implementing Iterator
for Foo
would mean that the instance itself would have change at each call to next()
, that's why iterators usually have their own struct.
If you want to provide iteration capability, I would advise adding an iter()
function which can return a std::slice::Iter
(which Vec::iter()
returns).
struct Foo {
bar: Vec<char>,
}
impl Foo {
pub fn iter(&self) -> impl Iterator<Item=&char> + '_ {
self.bar.iter()
}
}
Upvotes: 4