有没有办法让 Rust 闭包只将一些变量移入其中?

Qqw*_*qwy 15 multithreading closures lifetime move-semantics rust

我有一个通用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) {}
Run Code Online (Sandbox Code Playgroud)

这不编译:

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) {}
Run Code Online (Sandbox Code Playgroud)

这将无效,&settings因为第一次循环迭代将settingsmove闭包中取得所有权。

使其工作的唯一简单方法是:

  • Settings结构复制到每个线程中(这在我的实际应用程序中相当昂贵)
  • 介绍一个Arcaround settings,也感觉有点可惜。

有没有办法在这里绕过引用计数?有没有办法可以score在仍然允许引用的同时进入内部闭包settings

Qqw*_*qwy 11

是的,可以只将一个或一些变量移动到闭包中(而不是全部或全部)。

是的,这可以用来“规避”引用计数。

我在文档中找到了一个答案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();
    
    Run Code Online (Sandbox Code Playgroud)
  • 使用正常的闭包,并且只通过使用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();
    
    Run Code Online (Sandbox Code Playgroud)

  • [你的第二个答案不起作用](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=3fc1d15786092d676532e3b250f8c984)。 (4认同)
  • 这里有一个超级小的挑剔,但你并没有真正“规避”任何东西,你只是“不是”引用计数。 (2认同)

Tob*_*obu 7

倒闭!闭包箱中的宏能够选择性地引用、移动或克隆变量到闭包中。

取自文档的示例:

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| {
    ...
});
Run Code Online (Sandbox Code Playgroud)

捕获但未列出的变量默认会被移动。