As Fn
-like traits can be implemented multiple times, one can not do this:
trait Callable {
fn call_me(&self);
}
impl<F, T> Callable for F
where
F: Fn(T),
T: Default,
{
fn call_me(&self) {
self(T::default())
}
}
I'd like to do something like this, so I tried using trait objects as I can implement traits for them:
impl<T> Callable for dyn Fn(T)
where
T: Default,
{
fn call_me(&self, _: &Env) {
self(T::default())
}
}
This works, but I want to store all kinds of Callable
for a callback system. My first and best attempt was to use a Vec<Box<dyn Callable>>
, but it seems that trait objects can not be converted to trait objects of traits they implement.
trait Callable {
fn call_me(&self);
}
impl<T> Callable for dyn Fn(T)
where
T: Default,
{
fn call_me(&self) {
self(T::default())
}
}
impl<T> Callable for &dyn Fn(T)
where
T: Default,
{
fn call_me(&self) {
self(T::default())
}
}
fn main() {
let f = |n: i32| println!("n = {}", n);
// fine, obviously
f(i32::default());
// also fine
let bf = Box::new(f) as Box<dyn Fn(i32)>;
bf(i32::default());
// works if Callback implemented for &dyn Fn(T)
// but only works on references now
let bfc = &bf.as_ref() as &dyn Callable;
bfc.call_me();
// does not work
let callbacks: Vec<Box<dyn Callable>> = vec![bf as Box<dyn Callable>];
}
This gives me:
error[E0605]: non-primitive cast: `std::boxed::Box<dyn std::ops::Fn(i32)>` as `std::boxed::Box<dyn Callable>`
--> src/main.rs:39:50
|
39 | let callbacks: Vec<Box<dyn Callable>> = vec![bf as Box<dyn Callable>];
| ^^^^^^^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
I don't understand. Trait objects are types, I implemented a Callable
for all Fn(T)
trait objects, and you can normally coerce from a specific implemented trait object.
- Why is it not working?
- Is there a workaround?
- Is there a reason why
Fn
traits parameter types are generic and not associated?
Side note
I want this kind of system in order to store any closure (not only functions) whose parameters can be fetched from an environment, regardless of the number and types of parameters.
trait FromEnv {
fn from_env(env: &Env) -> Self;
}
trait Callable {
fn call_me(&self, env: &Env);
}
impl<T> Callable for Fn(T)
where
T: FromEnv,
{
fn call_me(&self, env: &Env) {
self(T::from_env(env))
}
}
// same kind of impl for Fn(T, U, ..., Z)
I know I can:
- allow closures which take
&Env
and let the user handle "extraction". - use a macro to create my own custom closure type which directly implements
Callable
.