0

I've found two ways to return closures from functions that use enclosing state:

fn closure_try(t: u64) -> Box<dyn Fn(u64) -> u64> {
    let f = |x: u64| x + t;
    return Box::new(f);
}

fn closure_try2(t: u64) -> impl Fn(u64) -> u64 {
    let f = move |x: u64| x + t.clone();
    return f;
}

Now, I need to store the output object into a struct and also be able to clone it.

Unfortunately, in the second instance, trait impl objects cannot be in struct position.

struct A {
    b: impl Fn(u64) -> u64
}

This fails with: impl Trait not allowed outside of function and method return types.

In the first example, the boxed dynamic trait object cannot be copied or cloned.

fn duplicate(f: Box<dyn Fn(u64) -> u64>) -> Box<dyn Fn(u64) -> u64> {
    f.clone()
}

This fails with the error "doesn't satisfy dyn Fn(u64) -> u64: Clone"

What would be an idiomatic way to create a store of closures where:

  • Closures can be duplicated
  • Each closure can capture some enclosing state

One option I'm considering is to have these functions return a closure that return the closure, but this seems problematic.

idomorphism
  • 117
  • 5
  • I don't understand this sentence: "in the second instance, trait `impl` objects cannot be in struct position". Can you show us some code that you have tried to "store the output object" and the corresponding error message? – Jmb Mar 31 '22 at 08:57
  • @Jmb The struct position language is what is used in rustc compiler issues. I've provided a code example of how to trigger the error. – idomorphism Mar 31 '22 at 08:59

1 Answers1

1

You can have a custom method to clone Box<dyn Trait>, see How to clone a struct storing a boxed trait object?:

trait ClonableFn: Fn(u64) -> u64 {
    fn clone_box(&self) -> Box<dyn ClonableFn>;
}
impl Clone for Box<dyn ClonableFn> {
    fn clone(&self) -> Self {
        <dyn ClonableFn>::clone_box(&**self)
    }
}
impl<T> ClonableFn for T
where
    T: 'static + Fn(u64) -> u64 + Clone,
{
    fn clone_box(&self) -> Box<dyn ClonableFn> {
        Box::new(T::clone(self))
    }
}

fn closure_try(t: u64) -> Box<dyn ClonableFn> {
    let f = move |x: u64| x + t;
    Box::new(f)
}

Playground.

Or, you can use the unstable feature type_alias_impl_trait to name the return type of the second method:

#![feature(type_alias_impl_trait)]

type ClosureType = impl Fn(u64) -> u64 + Clone;
fn closure_try(t: u64) -> ClosureType {
    move |x: u64| x + t
}

Playground

The latter approach is more performant, but nightly-only as of today.

Chayim Friedman
  • 47,971
  • 5
  • 48
  • 77
  • Thanks this is great! I'm following the latter approach but couldn't get it to work to create an array of closures. Added a separate question here: https://stackoverflow.com/questions/71689947/cannot-find-defining-use-when-declaring-closure-in-a-struct. – idomorphism Mar 31 '22 at 09:18