5 个不稳定版本
0.3.0 | 2020 年 3 月 30 日 |
---|---|
0.2.0 | 2018 年 8 月 4 日 |
0.1.2 | 2018 年 8 月 1 日 |
0.1.1 | 2018 年 8 月 1 日 |
0.1.0 | 2018 年 8 月 1 日 |
#554 in Rust 模式
11,344 每月下载量
在 22 个crate中使用(6个直接使用)
16KB
184 行
closure! - 一个用于逐变量捕获变量的宏
此crate提供了一个宏,允许您编写可以通过移动、引用、可变引用或克隆来单独捕获的闭包。
用法
首先,在您的Cargo.toml
中添加一个条目
[dependencies]
closure = "0.3.0"
然后您可以编写这样的闭包
use closure::closure;
let string = "move".to_string();
let x = 10;
let mut y = 20;
let rc = Rc::new(5);
let closure = closure!(move string, ref x, ref mut y, clone rc, |arg: i32| {
...
});
lib.rs
:
一个用于逐变量捕获变量的宏。
使用此宏,可以在指定的捕获列表中明确指定哪些变量将通过哪种方法被捕获。变量可以是移动、引用、可变引用或使用任意方法标识符(例如,clone
)转换。任何未明确指定的变量都将默认移动。
每种捕获类型的语法如下
move var
(将var
移动到闭包中)ref var
(借用var
)ref mut var
(可变借用var
)$IDENT var
(转换var
,其中$IDENT是任何具有self
接收器和没有其他参数的方法的标识符)
移动绑定
要捕获变量并将其移动到闭包中,请使用move
或move mut
来创建一个可变绑定
let first = "first".to_string();
let second = "second".to_string();
let closure = closure!(move first, move mut second, || {
// creates an immutable `first` and a mutable `second`
// binding...
# assert_eq!(first, "first");
# second.clear();
# assert_eq!(second, "");
});
引用绑定
要捕获变量并在闭包中借用它,请分别使用ref
或ref mut
来创建可变借用。
let mut a = 1;
let b = 0;
let mut closure = closure!(ref mut a, ref b, || {
*a = 0;
assert_eq!(*a, *b);
});
注意,也可以捕获结构体(包括结构体方法中的)的命名成员
struct Foo {
bar: i32,
}
impl Foo {
fn print(&self) {
// here a binding `let bar = &self.bar` will be
// created for the closure
closure!(ref self.bar, || println!("{}", bar))();
}
}
这同样适用于move
捕获,但适用常规的解构规则。
$IDENT-转换绑定
通过任意标识符捕获方法中的变量,例如使用 self
接收器(例如,self
,&self
,&mut self
等)且没有其他参数,会创建一个具有相同名称但应用了转换方法的原始变量的绑定。这种类型捕获的最常见用途可能是对变量调用 clone()
,但任何符合上述规则的方法也是可能的,例如 to_string
,to_owned
,into_iter
等。
let first = "first".to_string();
let second = "second".to_string();
let mut closure = closure!(clone first, clone mut second, || {
// creates two bindings `first` and `second`,
// the latter is mutable.
println!("cloned: {}", first);
second.clear();
# assert_eq!(second, "");
});
closure();
println!("the original {} and {} were not moved", first, second);
示例
启动线程
而不是必须编写
use std::thread;
use std::sync::{Arc, Barrier, Mutex};
let mutex = Arc::new(Mutex::new(Vec::new()));
let barrier = Arc::new(Barrier::new(2));
let vector_clone = Arc::clone(&mutex);
let barrier_clone = Arc::clone(&barrier);
thread::spawn(move || {
let mut vec = vector_clone.lock().unwrap();
vec.push(2);
vec.push(3);
vec.push(4);
barrier_clone.wait();
});
barrier.wait();
let mut vec = mutex.lock().unwrap();
vec.push(1);
assert_eq!(*vec, &[2, 3, 4, 1]);
使用 closure!
可以避免为每个克隆的 Arc
手动创建绑定
use std::thread;
use std::sync::{Arc, Barrier, Mutex};
use closure::closure;
let mutex = Arc::new(Mutex::new(Vec::new()));
let barrier = Arc::new(Barrier::new(2));
thread::spawn(closure!(clone mutex, clone barrier, || {
let mut vec = mutex.lock().unwrap();
vec.push(2);
vec.push(3);
vec.push(4);
barrier.wait();
}));
barrier.wait();
let mut vec = mutex.lock().unwrap();
vec.push(1);
assert_eq!(*vec, &[2, 3, 4, 1]);
将克隆的智能指针移动到线程闭包中
来自 Condvar
的文档
use std::sync::{Arc, Mutex, Condvar};
use std::thread;
let pair = Arc::new((Mutex::new(false), Condvar::new()));
let pair2 = pair.clone();
// Inside of our lock, spawn a new thread, and then wait for it to start.
thread::spawn(move|| {
let &(ref lock, ref cvar) = &*pair2;
let mut started = lock.lock().unwrap();
*started = true;
// We notify the condvar that the value has changed.
cvar.notify_one();
});
// Wait for the thread to start up.
let &(ref lock, ref cvar) = &*pair;
let mut started = lock.lock().unwrap();
while !*started {
started = cvar.wait(started).unwrap();
}
使用 closure!
可以避免显式声明 pair2
use std::sync::{Arc, Mutex, Condvar};
use std::thread;
use closure::closure;
let pair = Arc::new((Mutex::new(false), Condvar::new()));
// Inside of our lock, spawn a new thread, and then wait for it to start.
thread::spawn(closure!(clone pair, || {
let &(ref lock, ref cvar) = &*pair;
let mut started = lock.lock().unwrap();
*started = true;
// We notify the condvar that the value has changed.
cvar.notify_one();
}));
// Wait for the thread to start up.
let &(ref lock, ref cvar) = &*pair;
let mut started = lock.lock().unwrap();
while !*started {
started = cvar.wait(started).unwrap();
}
在不特别声明应保留引用的情况下混合移动和引用捕获
不应移动的引用
let move_string = "this string will be moved".to_string();
let mut ref_string = "this string will be borrowed".to_string();
let mut closure = closure!(move move_string, ref mut ref_string, || {
ref_string.push_str(&move_string);
//.. `move_string` is dropped at the end of the scope
});
在参数位置(即,在垂直线之间)和返回类型规范中也可以使用变量标识符,就像常规闭包一样。
限制
传递给宏的任何闭包将隐式成为 move
闭包,因此即使不在捕获列表中但用于闭包本身的变量也将被移动到其中。