Hi all -
The standard way to pass a closure to a function is with a signature of the form:
fn do_thing<F, T>(some: Thing, f: F) -> T
where F: Fn(Thing) -> T { f(some) }
But in Rust we generally avoid passing things by value unless there’s a real need; borrowing is preferred, which suggests this would be a better signature:
fn do_thing<F, T>(some: Thing, f: &F) -> T
where F: Fn(Thing) -> T { f(some) }
But given that it’s a borrow we can avoid a concrete type and pass a trait object:
fn do_thing<T>(some: Thing, f: &Fn(Thing) -> T) -> T { f(some) }
This seems like a much nicer signature, and much closer to what new users would expect to see. It avoids the need for a boxed trait object, so there’s no allocation to worry about. There’s no concrete type so it won’t be explicitly monomorphized, but that doesn’t mean the compiler won’t be able to find inlining opportunities, well, opportunistically.
The Closure chapter does cover this as “dynamic dispatch”, so this is more a question of emphasis. Monomorphization can be pretty expensive in terms of code size, which is a tradeoff that isn’t really covered, and I think the dynamic form is pretty unusual in the wild.