I'm a bit stumped on how to get this working, I've cut it down from the real thing. I wrote a trait:
pub trait Renderable<F: Fn(&PropertyTags)> {
fn set_property_changed_callback(&mut self, callback: Option<F>);
}
Which the 'child' parameter of add_child
is restricted by and PropertyTags
is just an enum. I've included mock implementations of the type of child
to demonstrate my usage:
pub struct Child<F: Fn(&PropertyTags)> {
property_changed_callback: Option<F>,
}
impl<F: Fn(&PropertyTags)> Renderable<F> for Child<F> {
fn set_property_changed_callback(&mut self, callback: Option<F>) {
self.property_changed_callback = callback;
}
}
Then these would be used as:
pub fn add_child<REND, C>(&mut self, child: &mut REND)
where C: Fn(&PropertyTags),
REND: Renderable<C>
{
let tc = Some(|property_tag: &PropertyTags|{
});
child.set_property_changed_callback(tc);
}
I'm getting the error:
child.set_property_changed_callback(tc);
| ^^ expected type parameter, found closure
|
= note: expected type `std::option::Option<C>`
= note: found type `std::option::Option<[closure@src/rendering/mod.rs:74:31: 76:18]>`
= help: here are some functions which might fulfill your needs:
- .take()
- .unwrap()
I've setup a minimal playground example which reproduces the issues here: https://play.rust-lang.org/?gist=bcc8d67f25ac620fe062032d8737954b&version=stable&backtrace=0
The problem is that add_child
claims to accept any Renderable<C>
, where C
can be any type that implements Fn(&PropertyTags)
, but then the function tries to give it a specific closure type that might not be the same as C
.
In order for this to work, add_child
's signature should look like this:
pub fn add_child<REND>(&mut self, child: &mut REND)
where REND: Renderable<AddChildCallback>
where AddChildCallback
is the name of a concrete type (that implements Fn(&PropertyTags)
).
The difficulty here is that on one hand, closure types don't have a name you can use in your Rust code, and on the other hand, implementing Fn
manually is unstable, so it requires a nightly compiler.
I'll also note that by making the callback type a type parameter, a Renderable
cannot be assigned a callback of a different type after a first callback has been set, as the first callback will determine the concrete type for the Renderable
. This might be fine for your usage, I just wanted to make sure you're aware of that.
If you want a solution that works on stable compilers (as of Rust 1.14.0), then you'll have to box the callback. add_child
's signature would then look like this:
pub fn add_child<REND>(&mut self, child: &mut REND)
where REND: Renderable<Box<Fn(&PropertyTags)>>
Here is an updated playground link with an example implementation of Fn
. Note that the parameters for call
, call_mut
and call_once
are passed as a tuple, as is required by the trait definition. The code is reproduced below for completeness:
struct RenderableCallback {
}
impl<'a> Fn<(&'a PropertyTags,)> for RenderableCallback {
extern "rust-call" fn call(&self, args: (&'a PropertyTags,)) -> Self::Output {
}
}
impl<'a> FnMut<(&'a PropertyTags,)> for RenderableCallback {
extern "rust-call" fn call_mut(&mut self, args: (&'a PropertyTags,)) -> Self::Output {
}
}
impl<'a> FnOnce<(&'a PropertyTags,)> for RenderableCallback {
type Output = ();
extern "rust-call" fn call_once(self, args: (&'a PropertyTags,)) -> Self::Output {
}
}