有没有办法让 Rust 闭包只将一些变量移入其中?
Posted
技术标签:
【中文标题】有没有办法让 Rust 闭包只将一些变量移入其中?【英文标题】:Is there a way to have a Rust closure that moves only some variables into it? 【发布时间】:2020-02-15 23:16:27 【问题描述】:我有一个通用的 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
?
【问题讨论】:
【参考方案1】: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|
...
);
已捕获但未列出的变量默认被移动。
【讨论】:
【参考方案2】:是的,可以只将一个或一些变量移动到闭包中(而不是全部或一个都没有)。
是的,这可以用来“规避”引用计数。
我在rayon::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();
【讨论】:
这里是极小的吹毛求疵,但你并没有真正“规避”任何事情,你只是不是引用计数。 @trentcl 谢谢!你当然是对的。 “规避”在这里可能有点草率。我稍微改变了答案中的书写方式,但如果您有更好的建议使其更清晰,请随时提出一个(和/或自己直接编辑答案) Your second answer does not work.以上是关于有没有办法让 Rust 闭包只将一些变量移入其中?的主要内容,如果未能解决你的问题,请参考以下文章
有没有办法从使用提供的 rust 中的 &str 类型获取函数指针