Macro capture_it::capture

source ·
macro_rules! capture {
    ([$($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);

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);