le_me
le_me

Reputation: 3419

How do I pass a function pointer recursively?

I want to write an Iterator adaptor which applies a function recursively to its underlying Iterator. Recursively because the variant IR::Loop includes a Vec<IR>, of which an iterator should also be passed to the function.

The function should take an &mut Iterator<Item = IR> and use it to compute the next value of the iterator, (like itertools::batching).

use std::iter::Peekable;

#[derive(Clone)]
enum IR {
    OperationA,
    OperationB,
    Loop(Vec<IR>),
}

pub trait MyItertools: Iterator {
    fn apply_recursive<F: Fn(&mut Peekable<Self>) -> Option<Self::Item>>(
        self,
        f: F,
    ) -> ApplyRecursive<Self, F>
    where
        Self: Sized,
        Self::Item: Clone,
    {
        ApplyRecursive {
            iter: self.peekable(),
            f: f,
        }
    }
}

impl<T: ?Sized> MyItertools for T
where
    T: Iterator,
{
}

//applies a function recursively to some Iterator with Item=IR
#[derive(Clone)]
struct ApplyRecursive<I, F>
where
    I: Iterator,
    I::Item: Clone,
{
    iter: Peekable<I>,
    f: F,
}

impl<I: Iterator<Item = IR>, F> Iterator for ApplyRecursive<I, F>
where
    F: Fn(&mut Peekable<I>)
       -> Option<I::Item>,
{
    type Item = I::Item;

    fn next(&mut self) -> Option<I::Item> {
        match self.iter.peek() {
            Some(&IR::Loop(code)) => {
                self.iter.next(); //advance the iterator
                let code: Vec<IR> = code.into_iter().apply_recursive(self.f).collect();

                Some(IR::Loop(code))
            }
            Some(x) => (self.f)(&mut self.iter),
            None => None,
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        self.iter.size_hint()
    }
}

fn main() {}

playground

What am I doing wrong? I don't even understand the error message:

error[E0277]: the trait bound `for<'r> F: std::ops::Fn<(&'r mut std::iter::Peekable<std::vec::IntoIter<IR>>,)>` is not satisfied
  --> src/main.rs:54:54
   |
54 |                 let code: Vec<IR> = code.into_iter().apply_recursive(self.f).collect();
   |                                                      ^^^^^^^^^^^^^^^ the trait `for<'r> std::ops::Fn<(&'r mut std::iter::Peekable<std::vec::IntoIter<IR>>,)>` is not implemented for `F`
   |
   = help: consider adding a `where for<'r> F: std::ops::Fn<(&'r mut std::iter::Peekable<std::vec::IntoIter<IR>>,)>` bound

error[E0277]: the trait bound `for<'r> F: std::ops::FnOnce<(&'r mut std::iter::Peekable<std::vec::IntoIter<IR>>,)>` is not satisfied
  --> src/main.rs:54:54
   |
54 |                 let code: Vec<IR> = code.into_iter().apply_recursive(self.f).collect();
   |                                                      ^^^^^^^^^^^^^^^ the trait `for<'r> std::ops::FnOnce<(&'r mut std::iter::Peekable<std::vec::IntoIter<IR>>,)>` is not implemented for `F`
   |
   = help: consider adding a `where for<'r> F: std::ops::FnOnce<(&'r mut std::iter::Peekable<std::vec::IntoIter<IR>>,)>` bound

error: no method named `collect` found for type `ApplyRecursive<std::vec::IntoIter<IR>, F>` in the current scope
  --> src/main.rs:54:78
   |
54 |                 let code: Vec<IR> = code.into_iter().apply_recursive(self.f).collect();
   |                                                                              ^^^^^^^
   |
   = note: the method `collect` exists but the following trait bounds were not satisfied: `F : std::ops::Fn<(&mut std::iter::Peekable<std::vec::IntoIter<IR>>,)>`, `ApplyRecursive<std::vec::IntoIter<IR>, F> : std::iter::Iterator`
   = help: items from traits can only be used if the trait is implemented and in scope; the following trait defines an item `collect`, perhaps you need to implement it:
   = help: candidate #1: `std::iter::Iterator`

Upvotes: 0

Views: 185

Answers (1)

Shepmaster
Shepmaster

Reputation: 430671

The last error indicates that you don't have an Iterator. Iterator is only implemented for your struct under certain conditions, and you aren't meeting them. The second error explains why.

the trait for<'r> Fn<(&'r mut IntoIter<IR>,)> is not implemented for the type F

So, why does the compiler think this won't work? Let's look at your constraints:

impl<I, F> Iterator for ApplyRecursive<I, F>
where
    I: Iterator<Item = IR>
    F: Fn(&mut Peekable<I>) -> Option<I::Item>,

This structure refers to a concrete type I that implements Iterator. Then F is a concrete type that accepts a mutable reference to the same concrete type as I. However, you try to use your function (specialized for whatever type it happens to be) on the concrete type IntoIter - but this might be a different concrete type!

The easiest fix is to remove the generics here:

impl<F> Iterator for ApplyRecursive<vec::IntoIter<IR>, F>
where
    F: Fn(&mut vec::IntoIter<IR>) -> Option<IR>,
{
    type Item = IR;

    fn next(&mut self) -> Option<IR> {

This unlocks a whole other slew of errors about mutability, accessing private fields, and exporting private types, but I think it gets over this hump.

Alternatively, we can change F to accept a trait object, and not worry about specializing it:

pub trait CustomIter: Iterator {
    fn apply_recursive<F>(self, f: F) -> ApplyRecursive<Self, F>
    where
        F: Fn(&mut Iterator<Item = Self::Item>) -> Option<Self::Item>,
        Self: Sized,
        Self::Item: Clone,
    {
        ApplyRecursive { iter: self.peekable(), f: f }
    }
}

impl<I, F> Iterator for ApplyRecursive<I, F>
where
    I: Iterator<Item = IR>,
    F: Fn(&mut Iterator<Item = IR>) -> Option<IR>,
{
    type Item = I::Item;

    fn next(&mut self) -> Option<IR> {

Upvotes: 2

Related Questions