Both of these functions accept anything that implements the Fn trait but how are they different?
Also, when do you think one might use one over the other?
Both allow polymorphism, but how and when they do that is quite different. A dyn Trait trait object is a concrete type enabling dynamic dispatch at run time. You cast other concrete types to dyn Trait and then you can pass fat pointers (&dyn Trait, Box<dyn Trait>, Arc<dyn Trait>, etc.—trait objects are dynamically sized like [T]) to your functions. impl Trait in argument position on the other hand is a form of compile time polymorphism. It's an anonymous generic type parameter. Something like fn foo(_: impl Debug) {} is pretty much syntactic sugar for fn foo<T: Debug>(_: T) {}. In a compile step called monomorphization calls to foo::<i32> and foo::<String> will lead to two distinct functions being compiled separately, avoiding any runtime dispatch overhead.
I know you know but for others: polymorphism was there it just did not require(/have) dyn. Thankfully dyn is compulsory. (Was a short period where could go either with or without.)
Another thing to keep in mind is that if the function accepts impl Fn(I) then the caller can pass &dyn Fn(I) to it, because &dyn Fn(I) implements Fn(I). So, impl Fn(I) (or <F: Fn(I)>) is the most flexible option for the caller — they get to choose dynamic or static dispatch.
Another difference is that I is part of dyn Fn(I) + ...'s type which participates in outlives bounds independently of the dyn lifetime, so the dyn type may not be 'static even though the erased type was (modulo unsafe).
fn assert_static<T: ?Sized + 'static>(_: &T) {}
// Works
fn test_impl<I, F: Fn(I) + 'static + Sync + Send>(f: F) {
assert_static(&f)
}
// error[E0310]: the parameter type `I` may not live long enough
fn test_dyn<I>(f: &(dyn Fn(I) + 'static + Sync + Send)) {
assert_static(f)
}
Another difference is that a &dyn _ parameter may be dyn compatible when a generic parameter would not be (although in the OP I is a separate generic anyway).
An implementor being 'static when the parameter is not 'static doesn't come up often, but can in niche circumstances.
Even if it didn't, it would be unsound for the dyn parameter to not participate in outlives bounds as the language is today, as that would permit transforming from dyn FnMut(&'static str) to dyn FnMut(&'whatever str) via dyn Any (for example).