2

How do you pass a closure to an object-safe trait method or otherwise via dynamic dispatch?

I have answered this myself, but the answer leaves something wanting: FnOnce closures must be boxed since they are not sized and must be consumed on use (thus cannot be passed by reference).

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
dhardy
  • 11,175
  • 7
  • 38
  • 46
  • 1
    "`FnOnce` closures must be boxed" this [has been possible since 1.35.0 (2019-05-23)](https://github.com/rust-lang/rust/blob/master/RELEASES.md#language-4) – mcarton Dec 10 '19 at 21:20
  • 1
    See also [“cannot move a value of type FnOnce” when moving a boxed function](https://stackoverflow.com/q/30411594/155423) – Shepmaster Dec 10 '19 at 21:34
  • It's rather unclear what you are asking. The code you have provided [compiles fine](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f3fd06fc369f80e835e1db4898591432) — what exactly are you attempting to demonstrate with it? Are you asking if there's some way of passing a `FnOnce` closure via dynamic dispatch without using a `Box` (and presumably not with another kind of heap-allocation)? – Shepmaster Dec 10 '19 at 21:36

2 Answers2

2

You can pass it without using Box:

fn pass_fn_once(do_thing: &dyn FnOnce()) {
    //do_thing();
}

fn main() {
    pass_fn_once(&|| println!("Hello!"));
}

However, you won't be able to actually call do_thing in pass_fn_once, since it is only borrowed - calling an FnOnce consumes it.

If they are Boxed, you can be sure that pass_fn_once took ownership so that the closure can be thrown away afterwards.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
phimuemue
  • 34,669
  • 9
  • 84
  • 115
2

Fn and FnMut closures can be passed by reference, as follows. FnOnce can be passed by reference but not called unless it is owned, e.g. by a Box.

fn pass_fn(get_num: &dyn Fn() -> i32) {
    let _num = get_num();
}

fn pass_fn_mut(set_num: &mut dyn FnMut(i32)) {
    set_num(6);
}

fn pass_fn_once(do_thing: Box<dyn FnOnce()>) {
    do_thing();
}

fn main() {
    pass_fn(&|| 2);

    let mut x = 1;
    pass_fn_mut(&mut |y| x = y);

    pass_fn_once(Box::new(|| println!("Hello!")));
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
dhardy
  • 11,175
  • 7
  • 38
  • 46