How to declare a function accepting a closure as argument whitch itself take another closure as argument?

I don't understant why the following code doesn't compile, and how to fix it. I want to create a function foo that take a closure closure as argument, witch itself accept a closure as argument. And I would like to have no Box or dyn.

pub fn foo<T, U>(closure: T)
    -> bool
where
    T: FnMut(U) -> bool,
    U: FnMut(i32) -> bool,
{
    closure(|i| true)    
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error: expected `;` or `{`, found keyword `fn`
 --> src/lib.rs:5:8
  |
5 |     U: fn(i32) -> bool,
  |        ^^ expected `;` or `{`

error: aborting due to previous error

error: could not compile `playground`.

To learn more, run the command again with --verbose.

So what fixed, finite size should the compiler allocate for the closure? The purpose of the box or dyn is to provide a fixed-size fat pointer, so that the compiler can determine how much memory to allocate on the stack.

You can't. This is one of the limitations of Rust, because we lack Higher Rank Type Bounds (HRTB) we can't quantify over all types. This means you can't have a generic closure that can take another closure without sime sort of dynamic dispatch

If we had HRTB, you could do something like:

pub fn foo<T>(closure: T)
    -> bool
where
    for<U: FnMut(i32) -> bool> T: FnMut(U) -> bool,
{
    closure(|i| true)    
}

But we don't have HRTB, so you must use dynamic dispatch instead

pub fn foo<T>(closure: T)
    -> bool
where
    for<'a> T: FnMut(&'a mut dyn FnMut(i32) -> bool) -> bool,
{
    closure(&mut |i| true)    
}
1 Like

You could define your own function trait:

trait TakesClosure {
    fn call<F>(&mut self, f: F) -> bool
    where
        F: FnMut(i32) -> bool;
}

playground

Of course this wont let you use a closure, and you have to manually create a struct and implement the trait, but it's the closest you can get without dynamic dispatch.

3 Likes

@RustyYato Thanks, it's what I feared. For some reason whatever I try to write anything generic in rust, I try to write something that depend on HRTB. I guess I liked black magic too much in C++! While we wait that the language get them, wouldn't it be possible for the compiler have a better explanation about this limitation, or would it be too complicated?

Is the dynamic dispatch in this specific situation going to be elided away by the compiler, or would the dyn imply a runtime penalty?

About the solution you gave me, do you have a link to the documentation of what the for is in a where clause? I am still really new to rust, and never saw it before. It is just a way to introduce a new generic argument?

And finally, why did you write the lifetime annotation? If I remove it, it still compiles.

The syntax T: for<'a> Trait<'a> syntax means that for every choice of lifetime 'a, T should implement Trait<'a>, and is known as a higher ranked trait bound. Currently this kind of "for every choice of x" bound is only supported for lifetimes and not types, but you were looking for something like "for every choice of type U", which does not currently exist in Rust.

Sure, you can elide it in this case, but the compiler turns it into a for<'a> bound behind the scenes again if you do so.

The compiler might inline it enough to notice that it doesn't need dynamic dispatch, but there's no guarantee.

Rayon has a real-life example of using a trait for generic callbacks: ProducerCallback.

1 Like

Thanks a lot everyone, I am learning a lot!

@alice is it possible to implement the Fn(i32) -> i32 trait for my struct (or whatever gives access to call syntax for a struct)? It would make the calling site nicer (foo(|f| f.call(32)) vs foo(|f| f(32))).

You can only implement the Fn traits on nightly.

I forgot to add the activate the related flags, it's why it didn't worked.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.