Closure accepting an Iterator as a parameter

I want to write a method which accepts a closure having a parameter which is an Iterator having some internal implementation. The below implementation of bar achieves this:

struct Foo {
    values: Vec<i32>,
}

impl Foo {
    pub fn bar(&self, f: impl FnOnce(&mut dyn Iterator<Item = i32>) -> i32) -> i32 {
        let mut iter = self.values.iter().map(|v| v + 1);
        f(&mut iter)
    }
}

Note that the functionality of the inner map is not important here. It is just to illustrate the fact that I have some internal Iterator generated by bar which I want the caller to be able to pass a closure which will operate on that Iterator.

My question: is there any way to replace FnOnce(&mut dyn Iterator<Item = i32>) with a concrete T like FnOnce(Map<...>) which would achieve the same thing without using a trait object? Or is there some other way to achieve this without using a trait object?

Not as-is, because you would have to express that the implementation of bar chooses the type, rather than the caller. So this is either an existential (which a trait object is) or a higher-kinded type with a generic closure (neither of which currently exists in Rust).

What you can do is turn the problem inside out and define your own generic callback trait and type(s). This forces the implementor of the callback trait to accept any type, and then the implementation of bar() can pass in whatever iterator it pleases to:

trait Callback {
    fn call<I>(self, iter: I) -> i32
    where
        I: Iterator<Item = i32>;
}

struct BarCallback;

impl Callback for BarCallback {
    fn call<I>(self, iter: I) -> i32
    where
        I: Iterator<Item = i32>
    {
        iter.sum()
    }
}

struct Foo {
    values: Vec<i32>,
}

impl Foo {
    pub fn bar<F: Callback>(&self, f: F) -> i32 {
        let iter = self.values.iter().map(|v| v + 1);
        f.call(iter)
    }
}
1 Like

Perfect, thank you! I guess this means there is no way for a caller to pass an anonymous closure, right? So I'm thinking one approach would be to provide two methods: one which takes a closure, and one a Callback implementation. This way if the user cares enough about performance they can use the Callback version. (I'm from a c# background and I'm just trying to figure out Rust ways of doing things I did in c#).

You can perform horrible macro hacks in order to inline the body of a closure into a Callback-implementing type created on the spot:

macro_rules! callback {
    (|$arg:ident| $body:expr) => {{
        struct __AutogenCallback;
        
        impl Callback for __AutogenCallback {
            fn call<I>(self, $arg: I) -> i32
            where
                I: Iterator<Item = i32>
            {
                $body
            }
        }
        
        __AutogenCallback
    }}
}

fn main() {
    let foo = Foo { values: vec![0, 1, 2] };
    let sum = foo.bar(callback!(|it| it.sum()));
    println!("{}", sum);
}
2 Likes

Oh, and by the way, you don't have to provide two methods. Just implement Callback for FnOnce(&mut dyn Iterator) -> i32:


impl<F> Callback for F
where
    F: FnOnce(&mut dyn Iterator<Item = i32>) -> i32
{
    fn call<I>(self, mut iter: I) -> i32
    where
        I: Iterator<Item = i32>
    {
        self(&mut iter)
    }
}
3 Likes

Interesting... implementing Callback for FnOnce(&mut dyn Iterator) -> i32 worked but required me to annotate the parameter type on the closure, i.e. foo.bar(|values: &mut dyn Iterator<Item = i32>| values.sum()). Makes sense, but also makes it slightly less user-friendly.

That's not too bad, though. If you want to, you can also invent a way to capture local variables as if in a real closure, e.g.:

macro_rules! callback {
    ($([$($capture:ident: $ty:ty $(= $init:expr)?),*])? $arg:ident $(: impl Iterator<Item = i32>)? => $body:expr) => {{
        #[allow(non_camel_case_types)]
        struct __AutogenCallback $(<$($capture),*>)? {
            $($($capture: $capture,)*)?
        }
        
        #[allow(unused_variables, unused_mut)]
        impl Callback for __AutogenCallback $(<$($ty),*>)? {
            fn call<I>(self, $arg: I) -> i32
            where
                I: Iterator<Item = i32>
            {
                let __AutogenCallback { $($(mut $capture),*)? } = self;
                $body
            }
        }
        
        __AutogenCallback $(::<$($ty),*>)? { $($($capture $(: $init)?),*)? }
    }}
}

This also does require type annotations, but overall, I think it's still shorter/more convenient to call at the use-site than manually implementing Callback.

1 Like