closure!() { /* proc-macro */ }
Expand description
Macro for creating a Closure
object. This is a wrapper around Closure::new
that
automatically type checks its arguments at run-time.
A Closure
takes Value
objects as inputs and output. This macro will automatically convert
the inputs to Rust types when invoking its callback, and then will convert the output back to a
Value
. All inputs must implement the FromValue
trait, and outputs must either implement
the ToValue
trait or be the unit type ()
. Type-checking of inputs is done at run-time; if
incorrect types are passed via Closure::invoke
then the closure will panic. Note that when
passing input types derived from Object
or Interface
, you must take care to upcast to
the exact object or interface type that is being received.
Similarly to clone!
, this macro can be useful in combination with signal
handlers to reduce boilerplate when passing references. Unique to Closure
objects is the
ability to watch an object using a the @watch
directive. Only an Object
value can be
passed to @watch
, and only one object can be watched per closure. When an object is watched,
a weak reference to the object is held in the closure. When the object is destroyed, the
closure will become invalidated: all signal handlers connected to the closure will become
disconnected, and any calls to Closure::invoke
on the closure will be silently ignored.
Internally, this is accomplished using Object::watch_closure
on the watched object.
The @weak-allow-none
and @strong
captures are also supported and behave the same as in
clone!
, as is aliasing captures with the as
keyword. Notably, these
captures are able to reference Rc
and Arc
values in addition to Object
values.
⚠️ IMPORTANT ⚠️
glib
needs to be in scope, so unless it’s one of the direct crate dependencies, you need to
import it because closure!
is using it. For example:
use gtk::glib;
Using as a closure object
use glib_macros::closure;
let concat_str = closure!(|s: &str| s.to_owned() + " World");
let result = concat_str.invoke::<String>(&[&"Hello"]);
assert_eq!(result, "Hello World");
Connecting to a signal
For wrapping closures that can’t be sent across threads, the
closure_local!
macro can be used. It has the same syntax as
closure!
, but instead uses Closure::new_local
internally.
use glib;
use glib::prelude::*;
use glib_macros::closure_local;
let obj = glib::Object::new::<glib::Object>(&[]);
obj.connect_closure(
"notify", false,
closure_local!(|_obj: glib::Object, pspec: glib::ParamSpec| {
println!("property notify: {}", pspec.name());
}));
Object Watching
use glib;
use glib::prelude::*;
use glib_macros::closure_local;
let closure = {
let obj = glib::Object::new::<glib::Object>(&[]);
let closure = closure_local!(@watch obj => move || {
obj.type_().name()
});
assert_eq!(closure.invoke::<String>(&[]), "GObject");
closure
};
// `obj` is dropped, closure invalidated so it always does nothing and returns None
closure.invoke::<()>(&[]);
@watch
has special behavior when connected to a signal:
use glib;
use glib::prelude::*;
use glib_macros::closure_local;
let obj = glib::Object::new::<glib::Object>(&[]);
{
let other = glib::Object::new::<glib::Object>(&[]);
obj.connect_closure(
"notify", false,
closure_local!(@watch other as b => move |a: glib::Object, pspec: glib::ParamSpec| {
let value = a.property_value(pspec.name());
b.set_property(pspec.name(), &value);
}));
// The signal handler will disconnect automatically at the end of this
// block when `other` is dropped.
}
Weak and Strong References
use glib;
use glib::prelude::*;
use glib_macros::closure;
use std::sync::Arc;
let closure = {
let a = Arc::new(String::from("Hello"));
let b = Arc::new(String::from("World"));
let c = "!";
let closure = closure!(@strong a, @weak-allow-none b, @to-owned c => move || {
// `a` is Arc<String>, `b` is Option<Arc<String>>, `c` is a `String`
format!("{} {}{}", a, b.as_ref().map(|b| b.as_str()).unwrap_or_else(|| "Moon"), c)
});
assert_eq!(closure.invoke::<String>(&[]), "Hello World!");
closure
};
// `a`, `c` still kept alive, `b` is dropped
assert_eq!(closure.invoke::<String>(&[]), "Hello Moon!");