closure_attr 0.3.0

An attribute macro to simplify closure captures
Documentation

closure_attr

This crate provides an attribute to simplify closure captures.

Example

use std::{rc::Rc, cell::Cell, cell::RefCell};

// Expects a 'static callback
fn use_callback<F: FnMut() + 'static>(mut callback: F) {
    callback();
}

#[closure_attr::with_closure] // Enable use of #[closure(...)]
fn example() {
    let s = Rc::new(RefCell::new(String::new()));
    let i = Rc::new(Cell::new(0));

    // The callback captures clones of s and i
    use_callback(
        #[closure(clone s, clone i)]
        move || {
            s.replace(format!("Hello, world! {}", i.get()));
            i.set(i.get() + 1);
        },
    );

    assert_eq!(s.borrow_mut().clone(), "Hello, world! 0");
    assert_eq!(i.get(), 1);
}

example();

It expands to:

use_callback({
    let s = s.clone(); // Clone requested by attribute
    let i = i.clone(); // Clone requested by attribute
    move || {
        {... code to force whole captures ...}
        s.replace(format!("Hello, world! {}", i.get()));
        i.set(i.get() + 1);
    }
});

Capture types

Syntax Description
clone <ident> Clone the variable
clone mut <ident> Clone the variable and make it mutable
ref <ident> Take a reference to the variable
ref mut <ident> Take a mutable reference to the variable
move <ident> Move the variable into the closure
move mut <ident> Move the variable into the closure and make it mutable
weak <ident> See below

weak

weak uses weak pointers to help break up reference cycles. It downgrades an Rc or Arc pointer (or anything which implements [Downgrade] and [Upgrade]) and captures it. The transformed closure upgrades the pointer when it is called. If any upgrade fails, it skips executing the body and returns Default::default().

use std::{rc::Rc, sync::Arc};

#[closure_attr::with_closure]
fn example() {
    let r = Rc::new(3);
    let a = Arc::new(4);

    let closure = #[closure(weak r, weak a)]
    move || *r * *a;

    assert_eq!(closure(), 12);
}

example();

This Expands to:

let closure = {
    let r = ::closure_attr::Downgrade::downgrade(&r);
    let a = ::closure_attr::Downgrade::downgrade(&a);
    move || {
        (|| {
            let r = ::closure_attr::Upgrade::upgrade(&r)?;
            let a = ::closure_attr::Upgrade::upgrade(&a)?;
            Some((|| *r * *a)())
        })()
        .unwrap_or_default()
    }
};

Whole captures

The capture attribute captures whole variables. For example, this code without the attribute produces an error:

fn send<T: Send>(_: T) {}

struct SendPointer(*const ());
unsafe impl Send for SendPointer {}

fn f() {
    let p = SendPointer(std::ptr::null());
    send(
        move || {
            p.0;
        },
    );
}
error[E0277]: `*const ()` cannot be sent between threads safely

A workaround:

#[closure_attr::with_closure]
fn f() {
    let p = SendPointer(std::ptr::null());
    send(
        #[closure(move p)]
        move || {
            p.0;
        },
    );
}

This is equivalent to inserting let _ = &p; into the body of the closure.

License

This work is dual-licensed under MIT and Apache 2.0. You can choose between one of them if you use this work.

SPDX-License-Identifier: MIT OR Apache-2.0