#[callback_helpers]
Expand description
Generate two helper functions to help working with cursive blueprints.
§Problem to solve
When writing cursive blueprints, it is often necessary to load parameters or variables.
Some of these have simple types like u64
or String
, but in some cases we need to load a
callback.
In this case, the blueprint loading the variable and the user storing the variable need to use the exact same type, or downcasting will not work. This is made complicated by Rust’s closures, where each closure is a unique anonymous type: if the user directly stores a closure, there will be no way to identify its exact type to downcast it in the blueprint.
Instead, both sides (blueprint and user) need to agree to use a fixed type, for example a trait
object like Arc<dyn Fn(&mut Cursive)>
(we use Arc
rather than Box
because we want variables
to be cloneable).
It’s a bit cumbersome having to write the exact type including the Arc
whenever we want to
store a callback for a blueprint. Similarly, it’s a bit annoying when writing the blueprint to make
sure the correct Arc<...>
type is fetched and converted to a type directly usable as callback.
§Solution
This is where this macro comes into play: from an original function that uses a callback, it generates two helper functions:
- A maker function, to be used when storing variables. This function takes a generic type
implementing the same
Fn
trait as the desired callback, and returns it wrapped in the correct trait object. - A setter function, to be used when writing blueprints. This function wraps the original
function, but takes a trait-object instead of a generic
Fn
type, and unwraps it internally.
§Notes
- The wrapped function doesn’t even have to take
self
, it can be a “static” constructor method. - The
maker
function always takes aFn
, not aFnMut
orFnOnce
. Use thecursive::immut1!
(and others) macros to wrap aFnMut
if you need it.
§Examples
struct Foo {
callback: Box<dyn Fn(&mut Cursive)>,
}
impl Foo {
#[cursive::callback_helpers]
pub fn new<F>(callback: F) -> Self
where
F: Fn(&mut Cursive) + 'static,
{
let callback = Box::new(callback);
Foo { callback }
}
}
cursive::blueprint!(Foo, |config, context| {
let foo =
Foo::new_with_cb(context.resolve(config["callback"])?);
Ok(foo)
});
// Elsewhere
fn foo() {
let mut context = cursive::builder::Context::new();
context.store("callback", Foo::new_cb(|s| s.quit()));
}