Implement Iterator trait for a struct containing an iterable field
Solution 1:
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.
Producing an iterator
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);
}
Writing an iterator
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);
}