It looks like a function in Rust can take a closure as argument as either a Box
:
closure: Box<dyn Fn()>
or as an &impl
:
closure: &impl Fn()
What are the differences between the two? And is there one that's recommended above the other?
It looks like a function in Rust can take a closure as argument as either a Box
:
closure: Box<dyn Fn()>
or as an &impl
:
closure: &impl Fn()
What are the differences between the two? And is there one that's recommended above the other?
Box<dyn Fn()>
is allocated on the heap, and uses dynamic dispatch.
Allocating on the heap is more expensive than stack allocation - memory has to be allocated for the object at runtime.
Dynamic dispatch means that the actual function to be run is determined at run time, as opposed to compile time.
impl Fn()
, when used in function argument position, is equivalent to using a generic parameter. For example, this:
foo(f: impl Fn())
is equivalent to:
foo<F: Fn()>(f: F)
Defined like this, Rust will use static dispatch. This means that it will generate a version of the function for each concrete type being pass into it, at compile time. This is trading larger compiled code size for lower run time cost, because it does not need to figure out which function to call at run time.
Most of the time, writing a function which takes impl Fn()
or the equivalent generic form would be preferred, because it leads to better run time performance.
A boxed closure also implements Fn()
so this works:
fn foo(f: impl Fn()) {
f();
}
fn main() {
let f = Box::new(|| println!("Hello"));
foo(f);
foo(|| println!("world"));
}
Writing your functions to take impl Fn()
is the most flexible since it can take either form of argument.
Note that using impl Fn()
in return position is different.
Also note both Box<dyn Fn()>
and impl Fn()
pass by value, so they transfer ownership of the closure. &impl Fn()
passes by reference - the equivalent boxed closure would be &Box<dyn Fn()>
.