There are a few reasons why it doesn't compile as-is.
First, the trait requires that the method takes a reference to self
, but in the closure case, you're not passing a reference. We can try just adding a reference...
But print_block(&|| ...)
also doesn't work. The error is talking about the trait not being implemeted for some specific closure. We'll return to here, but for now let's try casting.
&(|| ...) as &dyn Fn() -> String
still doesn't work. Now the compiler is complaining that it isn't 'static
. The problem this time is that dyn Trait
types always have a special lifetime, a "lifetime of applicability" if you will. You can almost always not write it out, but the default behavior you get when you elide it is very context sensitive and not always what you want. In this case, your implementation of the trait was only for dyn Fn() -> String + 'static
, for example.
So you can implement it for any lifetime instead, and now things work.
impl ParamDisplay for dyn std::ops::Fn() -> String + '_ {
// ^^^^
However, it's pretty unwieldly to call. Being generic in your implementation can relieve some of the pain though: dyn Fn() -> String + '_
is but a subset of all types that implement Fn() -> String
. If you change your implementation to be generic over types:
impl<F: Fn() -> String> ParamDisplay for F {
Then there is no implicit 'static
bound and you don't need to cast to a dyn Fn() -> String + '_
specifically. So now our first attempt at the callsite works:
print_block(&|| format!("param {}", x));
What if you wanted to get rid of explicitly taking a reference altogether? str
and dyn Trait
are unsized, so you can't pass them by value. But you can redefine the trait to take self
instead, and implement it for &str
and (generic) closures not behind a reference.
The ergonomics at the call site become much better, but you have lost some functionality:
-
ParamDisplay
is no longer dyn
-safe, and perhaps you need it to be
- You'll need a
+ Copy
or perhaps Clone
bound if you want to still be able to copy around generic implementing types the way you could shared references
One more approach, sort of half-way between, is to keep the parameter as &self
, but change print_block
to take by value, and add an implementation for &str
.
Ultimately, which approach is best will depend on your use case.