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 the #[watch]
attribute. 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]
, #[weak_allow_none]
, #[strong]
, #[to_owned]
captures are also supported and
behave the same as in clone!
, as is aliasing captures via rename_to
.
Similarly, upgrade failure of weak references can be adjusted via #[upgrade_or]
,
#[upgrade_or_else]
, #[upgrade_or_default]
and #[upgrade_or_panic]
.
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(rename_to = b)]
other,
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!");