为什么我不能将捕获的变量移动到闭包中?

问题描述 投票:0回答:1

这个片段:

struct TaskChain
{
    
}

impl TaskChain
{

    pub fn add_task<T, A>(mut self, job: T, arguments: A) -> Self
        where
            T: Fn(usize, A) + 'static,
            A: Clone + 'static,
    {
        let arg_copy = arguments.clone();
        self.run_task(
            move |num| { job(num, arg_copy) }
        );

        self
    }
    
    pub fn run_task<T>(&self, job: T)
    where
            T: Fn(usize) + 'static,
    {
        job(0)
    }
}

产生此错误:

error[E0507]: cannot move out of `arg_copy`, a captured variable in an `Fn` closure
  --> src/main.rs:16:35
   |
14 |         let arg_copy = arguments.clone();
   |             -------- captured outer variable
15 |         self.run_task(
16 |             move |num| { job(num, arg_copy) }
   |             ----------            ^^^^^^^^ move occurs because `arg_copy` has type `A`, which does not implement the `Copy` trait
   |             |
   |             captured by this `Fn` closure

我不明白为什么,我正在捕获变量,所以我不应该被允许将它进一步传递到闭包中吗?

rust closures borrow-checker
1个回答
0
投票

发生错误的原因是 Rust 阻止 arg_copy 变量多次移动到闭包中,因为它没有实现 Copy 特征。在 Rust 中,当您在 move 闭包中使用变量时,闭包将获得该变量的所有权。如果变量没有实现 Copy,则移动后将无法再次使用。

要解决此问题,请确保克隆闭包内的参数。这样,每次调用闭包时,它都会使用参数的新克隆,避免移动 arg_copy 的问题

pub fn add_task<T, A>(mut self, job: T, arguments: A) -> Self
    where
        T: Fn(usize, A) + 'static,
        A: Clone + 'static,
{
    self.run_task(
        move |num| {
            let arg_clone = arguments.clone(); // Clone inside the closure
            job(num, arg_clone)
        }
    );

    self
}

但是,此代码片段有一个错误,因为它尝试直接在 move 闭包内使用参数,这会尝试将参数移动到闭包中,从而导致编译错误,因为参数是在移动后使用的。

正确的方法是在闭包之外克隆参数,并确保每个克隆都得到正确使用:

pub fn add_task<T, A>(mut self, job: T, arguments: A) -> Self
    where
        T: Fn(usize, A) + 'static,
        A: Clone + 'static,
{
    let arg_clone = arguments.clone(); // Correctly clone outside the closure for use inside
    self.run_task(
        move |num| {
            job(num, arg_clone.clone()) // Ensure a fresh clone is used here if necessary
        }
    );

    self
}

根据 arg_clone 在作业中的使用方式,此更正后的版本可能仍面临逻辑问题。如果作业预计消耗 arg_clone,则此方法有效。如果没有,可能需要根据您工作职能的具体要求以及参数的使用方式进行进一步调整。

© www.soinside.com 2019 - 2024. All rights reserved.