Reputation: 7464
I have this code that I use in several places:
iterable.iter().map(|elem| f(elem, &mut acc))
where f
is a function like fn(T, &mut S) -> T
, taking an input, returning an output, and using the acc: &mut S
to store some intermediate state (e.g. a HashMap
). I wanted to refactor the code and create something like
struct MyIterator {
iter: ???
}
impl MyIterator {
fn new(iterable: Vec<T>, acc: &mut S) -> Self {
Iterator {
iter: iterable.iter().map(|elem| f(elem, &mut acc))
}
}
}
But I don't know what should be the type signature in ???
. Rust complains that closure cannot contain a captured variable.
I didn't want to overwhelm the question with unnecessary details, but I'll add an example as requested. I want MyIterator
to follow the Iterator
trait. Let's say that when iterating, at each self.next()
step I want to post-process the results produced self.iter.next()
. For example, f
returns Result<T, Error>
and I want an Err
to stop the iterator while keeping the error message.
struct MyIterator {
iter: ???
err: Result<(), ()>
}
impl MyItertor {
fn new(iterable: Vec<T>, acc: &mut S) -> Self {
MyIterator {
iter: iterable
.iter()
.map(|elem| f(elem, &mut ACC)),
err: Ok(()),
}
}
fn err(&self) -> Result<(), ()> {
self.err
}
}
impl Iterator for MyItertor {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
match self.iter.next() {
Ok(result) => result,
Err(err) => {
self.err = Err(err);
None
},
}
}
}
As for the types, it can be anything, making the code work for a trivial case like below will help me move along.
type T = i64;
type S = i64;
fn f(x: &T, acc: &mut S) -> Result<T, ()> {
let y = x + *acc;
*acc = y;
if *acc > 100 {
return Err(())
}
Ok(y)
}
Upvotes: 0
Views: 436
Reputation: 7579
First, you can make MyIterator
generic over the type of the wrapped iterator:
struct MyIterator<I> {
iter: I,
err: Result<(), ()>,
}
Then, you can declare a free-standing my_iter
function to serve as the constructor for a MyIterator
. It returns a MyIterator
parameterized over an anonymous iterator type via the impl Trait
syntax:
fn my_iter<'a>(
iterable: &'a Vec<T>,
acc: &'a mut S,
) -> MyIterator<impl Iterator<Item = Result<T, ()>> + 'a> {
MyIterator {
iter: iterable.iter().map(|elem| f(elem, acc)),
err: Ok(()),
}
}
impl<I> MyIterator<I> {
fn err(&self) -> Result<(), ()> {
self.err
}
}
Note that the associated type Iterator::Item
in the return type must be equal to the return type of f
. Also, my_iter
is not placed in the MyIterator
impl only because the function is not constrained by the type parameter I
of the impl
.
Iterator
can then be implemented for MyIterator
objects wrapping the type of iterator you need (i.e. the same type of iterator as created in my_iter
):
impl<T, I: Iterator<Item=Result<T, ()>>> Iterator for MyIterator<I> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
match self.iter.next() {
Some(Ok(result)) => Some(result),
Some(Err(err)) => {
self.err = Err(err);
None
},
None => None,
}
}
}
Upvotes: 1