Tim
Tim

Reputation: 7464

Type annotation for a closure

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.

Example

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

Answers (1)

EvilTak
EvilTak

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,
        }
    }
}

Playground

Upvotes: 1

Related Questions