有没有办法让 Rust 闭包只将一些变量移入其中?
Is there a way to have a Rust closure that moves only some variables into it?
我有一个通用的 struct
设置和一个我想调整和使用的额外变量设置。
对于整数范围内的所有可能值,我想启动一个(范围)线程,并将此变量设置为该值。根据这个值,他们做的工作略有不同。
这些线程中的每一个都应该能够读取通用设置结构。
use crossbeam; // 0.7.3
struct Settings {
// ... many fields
}
const MAX_FEASIBLE_SCORE: u8 = 10;
fn example(settings: Settings) {
crossbeam::scope(|scope| {
for score in 0..MAX_FEASIBLE_SCORE {
scope.spawn(|_| {
let work_result = do_cool_computation(&settings, score);
println!("{:?}", work_result);
});
}
})
.unwrap();
}
fn do_cool_computation(_: &Settings, _: u8) {}
这不编译:
error[E0373]: closure may outlive the current function, but it borrows `score`, which is owned by the current function
--> src/lib.rs:12:25
|
10 | crossbeam::scope(|scope| {
| ----- has type `&crossbeam_utils::thread::Scope<'1>`
11 | for score in 0..MAX_FEASIBLE_SCORE {
12 | scope.spawn(|_| {
| ^^^ may outlive borrowed value `score`
13 | let work_result = do_cool_computation(&settings, score);
| ----- `score` is borrowed here
|
note: function requires argument type to outlive `'1`
--> src/lib.rs:12:13
|
12 | / scope.spawn(|_| {
13 | | let work_result = do_cool_computation(&settings, score);
14 | | println!("{:?}", work_result);
15 | | });
| |______________^
help: to force the closure to take ownership of `score` (and any other referenced variables), use the `move` keyword
|
12 | scope.spawn(move |_| {
| ^^^^^^^^
这将使 &settings
无效,因为第一个循环迭代将在 move
闭包中取得 settings
的所有权。
让它发挥作用的唯一简单方法是:
- 将
Settings
结构复制到每个线程中(这在我的实际应用程序中相当昂贵)
- 在
settings
周围引入一个Arc
,感觉也有点可惜。
有什么办法可以绕过这里的引用计数吗?有没有一种方法可以将 score
移动到内部闭包中,同时仍然允许引用 settings
?
是的,可以只将一个或一些变量移动到闭包中(而不是全部或none)。
是的,这可以用于"circumvent"引用计数。
我在 rayon::scope
的文档中找到了一个答案,结果正是关于这个问题:'Accessing the stack data [from within a scoped threads scope]'。该页面还有一个例子,比这个问题中的伪代码更清楚。
事实证明你可以:
使用 move
闭包,但通过使用引用遮蔽它们来引用外部作用域中的变量,因此使用 let settings = &settings
通过引用而不是通过值来捕获它们:
crossbeam::scope(|scope| {
let settings = &settings; // refer to outer variable by reference
for score in 0..MAX_FEASIBLE_SCORE {
scope.spawn(move |_| {
let work_result = do_cool_computation(settings, score);
println!("{:?}", work_result);
});
}
})
.unwrap();
使用普通闭包,仅通过使用 let score = score
:
将所需变量隐藏在闭包内来移动它们
crossbeam::scope(|scope| {
for score in 0..MAX_FEASIBLE_SCORE {
scope.spawn(|_| {
let score = score; // capture only score
let work_result = do_cool_computation(&settings, score);
println!("{:?}", work_result);
});
}
})
.unwrap();
closure! macro 提供了有选择地引用、移动或克隆变量到闭包中的能力。
取自文档的示例:
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| {
...
});
捕获但未列出的变量默认被移动。
我有一个通用的 struct
设置和一个我想调整和使用的额外变量设置。
对于整数范围内的所有可能值,我想启动一个(范围)线程,并将此变量设置为该值。根据这个值,他们做的工作略有不同。
这些线程中的每一个都应该能够读取通用设置结构。
use crossbeam; // 0.7.3
struct Settings {
// ... many fields
}
const MAX_FEASIBLE_SCORE: u8 = 10;
fn example(settings: Settings) {
crossbeam::scope(|scope| {
for score in 0..MAX_FEASIBLE_SCORE {
scope.spawn(|_| {
let work_result = do_cool_computation(&settings, score);
println!("{:?}", work_result);
});
}
})
.unwrap();
}
fn do_cool_computation(_: &Settings, _: u8) {}
这不编译:
error[E0373]: closure may outlive the current function, but it borrows `score`, which is owned by the current function
--> src/lib.rs:12:25
|
10 | crossbeam::scope(|scope| {
| ----- has type `&crossbeam_utils::thread::Scope<'1>`
11 | for score in 0..MAX_FEASIBLE_SCORE {
12 | scope.spawn(|_| {
| ^^^ may outlive borrowed value `score`
13 | let work_result = do_cool_computation(&settings, score);
| ----- `score` is borrowed here
|
note: function requires argument type to outlive `'1`
--> src/lib.rs:12:13
|
12 | / scope.spawn(|_| {
13 | | let work_result = do_cool_computation(&settings, score);
14 | | println!("{:?}", work_result);
15 | | });
| |______________^
help: to force the closure to take ownership of `score` (and any other referenced variables), use the `move` keyword
|
12 | scope.spawn(move |_| {
| ^^^^^^^^
这将使 &settings
无效,因为第一个循环迭代将在 move
闭包中取得 settings
的所有权。
让它发挥作用的唯一简单方法是:
- 将
Settings
结构复制到每个线程中(这在我的实际应用程序中相当昂贵) - 在
settings
周围引入一个Arc
,感觉也有点可惜。
有什么办法可以绕过这里的引用计数吗?有没有一种方法可以将 score
移动到内部闭包中,同时仍然允许引用 settings
?
是的,可以只将一个或一些变量移动到闭包中(而不是全部或none)。
是的,这可以用于"circumvent"引用计数。
我在 rayon::scope
的文档中找到了一个答案,结果正是关于这个问题:'Accessing the stack data [from within a scoped threads scope]'。该页面还有一个例子,比这个问题中的伪代码更清楚。
事实证明你可以:
使用
move
闭包,但通过使用引用遮蔽它们来引用外部作用域中的变量,因此使用let settings = &settings
通过引用而不是通过值来捕获它们:crossbeam::scope(|scope| { let settings = &settings; // refer to outer variable by reference for score in 0..MAX_FEASIBLE_SCORE { scope.spawn(move |_| { let work_result = do_cool_computation(settings, score); println!("{:?}", work_result); }); } }) .unwrap();
使用普通闭包,仅通过使用
将所需变量隐藏在闭包内来移动它们let score = score
:crossbeam::scope(|scope| { for score in 0..MAX_FEASIBLE_SCORE { scope.spawn(|_| { let score = score; // capture only score let work_result = do_cool_computation(&settings, score); println!("{:?}", work_result); }); } }) .unwrap();
closure! macro 提供了有选择地引用、移动或克隆变量到闭包中的能力。
取自文档的示例:
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| {
...
});
捕获但未列出的变量默认被移动。