#闭包 #捕获 #

闭包

一个用于逐变量捕获变量的宏

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 模式

Download history 3686/week @ 2023-11-27 4002/week @ 2023-12-04 3826/week @ 2023-12-11 1008/week @ 2023-12-18 833/week @ 2023-12-25 3670/week @ 2024-01-01 3146/week @ 2024-01-08 2205/week @ 2024-01-15 2996/week @ 2024-01-22 2757/week @ 2024-01-29 1943/week @ 2024-02-05 2387/week @ 2024-02-12 2552/week @ 2024-02-19 2779/week @ 2024-02-26 3235/week @ 2024-03-04 2605/week @ 2024-03-11

11,344 每月下载量
22 个crate中使用(6个直接使用)

MIT 许可证

16KB
184

closure! - 一个用于逐变量捕获变量的宏

Latest version Documentation License

此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接收器和没有其他参数的方法的标识符)

移动绑定

要捕获变量并将其移动到闭包中,请使用movemove 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, "");
});

引用绑定

要捕获变量并在闭包中借用它,请分别使用refref 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_stringto_ownedinto_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 闭包,因此即使不在捕获列表中但用于闭包本身的变量也将被移动到其中。

无运行时依赖