Rayon 错误,不能借用“*self”作为可变的,因为它是“Fn”闭包中捕获的变量
首先,我是 Rust 新手。对于我正在开发的游戏,我需要调用函数来更改矢量上的像素,然后将其转换为图像。为此,我有一个包含游戏所有上下文的结构,包括所述图像以及计算像素所需的所有内容。
这适用于单线程,但当我尝试将其与 Rayon 并行时则不行。
这是一个简化的代码作为问题的示例。它与 chunks_mut
一起使用。
use ::rayon::prelude::*;
struct Game {
pub pixel: [u8; 4],
pub pixel_vec: Vec<u8>,
}
impl Game {
pub fn new(pixel: [u8; 4], length: usize) -> Self {
let pixel_vec = vec![2; length];
Self { pixel, pixel_vec }
}
pub fn draw(&mut self) {
let mut pixel_vec = std::mem::take(&mut self.pixel_vec);
pixel_vec
.par_chunks_mut(4)
.for_each(|slice| self.some_function(slice));
self.pixel_vec = pixel_vec;
}
fn some_function(&mut self, slice: &mut [u8]) {
for i in 0..slice.len() {
slice[i] = self.pixel[i] * self.pixel[i];
}
}
}
fn main() {
let mut game = Game::new([0, 3, 0, 0], 20);
game.draw();
}
错误
Compiling playground v0.0.1 (/playground)
error[E0596]: cannot borrow `*self` as mutable, as it is a captured variable in a `Fn` closure
--> src/main.rs:18:31
|
18 | .for_each(|slice| self.some_function(slice));
| ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
For more information about this error, try `rustc --explain E0596`.
error: could not compile `playground` due to previous error
First of all, I'm new toRust. For a game I'm working on I need to call function to change pixels on a vector that is then transformed into an iamge. For that, I have a struct that contains all the context of the game, include said image and everything I need to calcaulate the pixels.
This works with a single thread but not when I try to parallelize it with Rayon.
Here's a simplified code as an example of the problem. It works with chunks_mut
.
use ::rayon::prelude::*;
struct Game {
pub pixel: [u8; 4],
pub pixel_vec: Vec<u8>,
}
impl Game {
pub fn new(pixel: [u8; 4], length: usize) -> Self {
let pixel_vec = vec![2; length];
Self { pixel, pixel_vec }
}
pub fn draw(&mut self) {
let mut pixel_vec = std::mem::take(&mut self.pixel_vec);
pixel_vec
.par_chunks_mut(4)
.for_each(|slice| self.some_function(slice));
self.pixel_vec = pixel_vec;
}
fn some_function(&mut self, slice: &mut [u8]) {
for i in 0..slice.len() {
slice[i] = self.pixel[i] * self.pixel[i];
}
}
}
fn main() {
let mut game = Game::new([0, 3, 0, 0], 20);
game.draw();
}
Error
Compiling playground v0.0.1 (/playground)
error[E0596]: cannot borrow `*self` as mutable, as it is a captured variable in a `Fn` closure
--> src/main.rs:18:31
|
18 | .for_each(|slice| self.some_function(slice));
| ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
For more information about this error, try `rustc --explain E0596`.
error: could not compile `playground` due to previous error
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您的
Game::some_function
函数使用&mut self
接收器。这意味着它必须可变地借用self
。也就是说,self.some_function(slice)
与Game::some_function(&mut self, slice)
相同。因此,当您在闭包中调用
self.some_function(slice)
时,闭包必须将self
捕获为可变的,因为它需要借用self
作为调用该函数是可变的。但是,for_each
函数需要一个Fn
闭包,而Fn
闭包不会让您捕获为可变的(有充分的理由)。您的情况的解决方案是将
some_function
中的接收器更改为&self
,因为按照编写,该函数不需要对self
进行可变访问>:编辑:
要完成解释:人造丝
par_chunks_mut
方法返回一个实现ParallelIterator
,这是for_each
方法的来源。这就是需要实现Fn
的参数的地方。现在考虑 Rust 中的可用的闭包特征,为什么是
Fn
?其他选项是 FnOnce 或 FnMut。 FnOnce 在调用一次时会消耗闭包,因此最多只能调用一次(因此得名),显然这不适用于 for_each 方法。FnMut
较弱,它可以可变地捕获变量,那么为什么 rayon 不使用它呢?重点是在不同的线程中同时调用这个闭包,如果你改变它的状态,你就不能这样做,你必须同步可变访问,这将有效地序列化操作,使整个事情浪费时间。因此,Fn
是唯一的选项,并且不允许进行可变捕获。Your
Game::some_function
function uses the&mut self
receiver. This means that it must borrowself
mutably. That is,self.some_function(slice)
is the same asGame::some_function(&mut self, slice)
.So when you call
self.some_function(slice)
in your closure, the closure must captureself
as mutable, since it needs to borrowself
as mutable to call the function. However, thefor_each
function requires aFn
closure and aFn
closure will not let you capture as mutable (for good reason).The solution in your case is to change the receiver in
some_function
to&self
, since as written that function doesn't need mutable access toself
:Edit:
To complete the explanation: the rayon
par_chunks_mut
method returns a type that implementsParallelIterator
, which is where thefor_each
method comes from. That's where the argument implementingFn
is required.Now thinking about the available closure traits in Rust, why
Fn
? Well the other options areFnOnce
orFnMut
.FnOnce
consumes the closure when called once and thus can only be called at most once (hence the name), clearly this isn't going to work for afor_each
method.FnMut
is weaker, it can capture variables mutably, so why does rayon not use that? The entire point is to call this closure concurrently in different threads, which you can't do if you're mutating its state, you'd have to synchronise the mutable access which would effectively serialise the operation, making the whole thing a waste of time. Thus,Fn
is the only option and no mutable captures are allowed.