4 releases (breaking)
0.4.0 | Sep 8, 2023 |
---|---|
0.3.0 | Sep 8, 2023 |
0.2.0 | Sep 7, 2023 |
0.1.0 | Sep 6, 2023 |
#1904 in Rust patterns
9KB
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> |
Downgrade an Rc , Arc , or anything else which implements [Downgrade]. Captures the downgraded pointer. This helps break up reference loops. |
fail(<expr>) <ident> |
Like weak , but upgrades the weak pointer before executing the closure body. If the upgrade fails, it skips executing the body and returns the expression. |
panic <ident> |
Like weak , but upgrades the weak pointer before executing the closure body. If the upgrade fails, it panics with message "Closure failed to upgrade weak pointer". |
weak
, fail
, and panic
transforms
use std::{rc::Rc, cell::Cell, cell::RefCell};
#[closure_attr::with_closure]
fn weak_examples() {
let i = Rc::new(42);
let weak = #[closure(weak i)]
move || *i.upgrade().unwrap() + 1; // manual upgrade
let fail = #[closure(fail(7) i)]
move || *i + 2;
let panic = #[closure(panic i)]
move || *i + 3;
assert_eq!(weak(), 43);
assert_eq!(fail(), 44);
assert_eq!(panic(), 45);
}
weak_examples();
The closures expand to:
let weak = {
let i = ::closure_attr::Downgrade::downgrade(&i);
move || *i.upgrade().unwrap() + 1 // manual upgrade
};
let fail = {
let i = ::closure_attr::Downgrade::downgrade(&i);
move || {
let Some(i) = ::closure_attr::Upgrade::upgrade(&i) else {
return 7;
};
*i + 2
}
};
let panic = {
let i = ::closure_attr::Downgrade::downgrade(&i);
move || {
let Some(i) = ::closure_attr::Upgrade::upgrade(&i) else {
::std::panic!("Closure failed to upgrade weak pointer");
};
*i + 3
}
};
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
Dependencies
~235–680KB
~16K SLoC