Polkadot's code passes around Box<dyn futures::task::Spawner> objects and uses them to spawn tasks.
The object inside of these Box<dyn Spawner> is generally a sc_service::SpawnTaskHandler (https://docs.rs/sc-service/0.8.0-rc3/sc_service/struct.SpawnTaskHandle.html) if I'm not mistaken. The SpawnTaskHandler wraps around the tasks that are spawned and adds diagnostics to them: their CPU usage, how long polling took, and so on.
The problem is that if you use the futures::Spawner trait, the tasks spawned are reported as "unnamed" on Prometheus/Grafana.
Instead, it would be great to use SpawnTaskHandle::spawn, which accepts a name as parameter.
In Substrate, the "semi-idiomatic" thing we've been doing is to pass around Box<dyn Fn(&'static str, Box<dyn Future<Output = ()> + Send>)> objects, instead of Box<dyn Spawner>.
Passing around a closure rather than a trait also has the advantage that it's easier to implement.