macro_rules! capture {
([$($args:tt)*], $($closure:tt)*) => { ... };
([$($args:tt)*]..$($closure:tt)*) => { ... };
}Expand description
Generate a closure that captures specified variables, and captures all other unspecified variables by move.
The first argument to the macro is always a list of capture arguments wrapped in [], and you pass a closure or async block to the second argument, separated by commas. In this case, the second argument must be explicitly specified as move capture.
§Usage
§capture by copy
If you specify a variable name in square brackets, the Clone::clone function is called
against that variable, creating a variable of the same name and capturing it with the specified
closure. (Capturing the variable is always guaranteed, even if you don’t explicitly use it
within the block.)
let a = 1;
let closure = capture_it::capture!([a], move || { a });
assert_eq!(closure(), 1);All of these rules, except for reference capture (the & prefix), which we’ll discuss later, can be declared mutable by prefixing the variable name with * (an asterisk).
(NOTE: We originally wanted to use the mut keyword prefix, but that would cause rustfmt to
consider the macro expression as unjustified rust code and disable formatting, so we were forced
to adopt this unorthodox method)
let count = 0;
let mut closure = capture_it::capture!([*count], move || { count += 1; count });
assert_eq!(closure(), 1);
assert_eq!(closure(), 2);
assert_eq!(closure(), 3);
assert_eq!(count, 0); // as it was copied ...§capture by reference
You can explicitly reference-capture a variable by prefixing its name with & or &mut. (Note that all variables not specified in the capture list will be MOVE-captured, as only blocks with a MOVE policy are allowed as second arguments. Any variables you want to capture by reference must be explicitly specified in the capture list)
let a = std::cell::Cell::new(1);
let closure = capture_it::capture!([&a], move || { a.get() });
a.set(2);
assert_eq!(closure(), 2);§capture by alias
Similar to the lambda capture rules in modern C++, it is possible to capture an expression by giving it an alias.
let mut closure = capture_it::capture!([*a = 0], move || { a += 1; a });
assert_eq!(closure(), 1);
assert_eq!(closure(), 2);
assert_eq!(closure(), 3);§capture struct fields
Under limited conditions, you can capture struct fields. The following expressions will capture each struct field as a copy and a reference, respectively.
struct Foo {
copied: i32,
borrowed: std::cell::Cell<i32>,
}
let mut foo = Foo { copied: 1, borrowed: 2.into() };
let closure = capture_it::capture!([foo.copied, &foo.borrowed], move || {
copied + borrowed.get()
});
foo.copied = 9999;
foo.borrowed.set(3);
assert_eq!(closure(), 4);§async blocks
All rules apply equally to the async move block.
let mut copied = 1;
let borrowed = std::cell::Cell::new(2);
let task = capture_it::capture!([copied, &borrowed], async move {
copied + borrowed.get()
});
copied = 9999;
borrowed.set(3);
let val = futures::executor::block_on(task);
assert_eq!(val, 4);§Shortcuts
There are various shortcuts for capturing variables for convenience.
Own: Call theToOwned::to_ownedmethod on the target variable.Weak: Downgrades astd::rc::Rcorstd::sync::Arctype to a Weak reference.Some: Wrap cloned variable withOption. This is useful when you have to retrieve captured variable when it’s not aFnOnceclosure.- All paths that do not fall under the above rules (
$($p:ident)::*) are replaced with function calls. - You can simply write single method invocation on the captured variable, to capture the result
of return value.
capture!([foo.bar()], move || { ... })-> in this case,let foo = foo.bar()will be captured into the closure.
#[cfg(not(feature = "no-std"))]
{
use capture_it::capture;
let hello = "hello, world!";
let rc = std::rc::Rc::new(());
let arc = std::sync::Arc::new(());
let arc_2 = arc.clone();
let hello_other = "hello, other!";
let closure = capture!([Own(hello), Weak(rc), Weak(arc), arc_2, *hello_other.to_string()], move || {
assert_eq!(hello, "hello, world!");
assert!(rc.upgrade().is_none());
assert!(arc.upgrade().is_some());
assert_eq!(hello_other, "hello, other!");
});
drop((rc, arc));
closure();
}