How to return a function from a function for the purpose of parameter injection?

Viewed 106

I am trying to return a function from another function.

// Immediately run function passed in
fn set_timeout(f: &dyn Fn()) {
    f();
}

fn callback() -> dyn Fn() {
    return fn() {
        println!("hi");
    };
}

fn main() {
  set_timeout(&callback());
}

My end goal is to achieve a long form variation of the following code where I move fooRef1 into the inner function through a parameter on callback(fooRef1) (parameter injection).

fn main() {
  let fooRef = Arc::new(Mutex::new(String::from("foo")));
  let fooRef1 = fooRef.clone();

  set_timeout(&(move || {
    let mut foo = fooRef1.lock().unwrap();
    foo.push_str("bar");
    println!("{}", foo);
  }));
}

Why does the inline callback closure work but the separated function does not?

EDIT: Here's the error messages from rustc, as obtained via Rust playground.

   Compiling playground v0.0.1 (/playground)
error: expected one of `.`, `;`, `?`, `}`, or an operator, found keyword `fn`
 --> src/main.rs:7:12
  |
7 |     return fn() {
  |            ^^ expected one of `.`, `;`, `?`, `}`, or an operator

error[E0746]: return type cannot have an unboxed trait object
 --> src/main.rs:6:18
  |
6 | fn callback() -> dyn Fn() {
  |                  ^^^^^^^^ doesn't have a size known at compile-time
  |
  = note: for information on trait objects, see <https://doc.rust-lang.org/book/ch17-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types>
  = note: if all the returned values were of the same type you could use `impl Fn()` as the return type
  = note: for information on `impl Trait`, see <https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits>
  = note: you can create a new `enum` with a variant for each returned type
help: return a boxed trait object instead
  |
6 | fn callback() -> Box<dyn Fn()> {
7 |     Box::new(return fn() {
8 |         println!("hi");
9 |     };)
  |

error[E0277]: the size for values of type `(dyn Fn() + 'static)` cannot be known at compilation time
  --> src/main.rs:13:16
   |
13 |   set_timeout(&callback());
   |                ^^^^^^^^ doesn't have a size known at compile-time
   |
   = help: the trait `Sized` is not implemented for `(dyn Fn() + 'static)`
   = note: the return type of a function must have a statically known size

Some errors have detailed explanations: E0277, E0746.
For more information about an error, try `rustc --explain E0277`.
error: could not compile `playground` due to 3 previous errors
1 Answers

So, looking at the errors from rustc, the first problem is that return expects something other than a function declaration. Instead, as pointed out in the comment by @peter-hall you have to define the function first, and then just use it as the return value:

def outer() -> fn() {
  fn inner() {
    //blabla
  }
  inner
}

And then the next problems are about dyn, and that you can't just return a naked dyn object.

One of the solutions to this is pointed out by the compiler: Put your trait object into a Box, i.e., allocate it on the heap.

Alternatively, use the impl Trait syntax: https://doc.rust-lang.org/rust-by-example/trait/impl_trait.html

Or just state that your return type is that of fn. Credit to those suggestions all to peter-hall.

Related