无法移出"Fn"闭包中捕获的外部变量

nat*_*ore 18 function-pointers channel rust

我试图弄清楚如何通过一个通道发送一个函数,以及如何避免额外的克隆,以便在另一端执行该函数.如果我在闭包内删除额外的克隆操作,我会收到以下错误:

error: cannot move out of captured outer variable in an 'Fn' closure
Run Code Online (Sandbox Code Playgroud)

忽略这个代码绝对没有任何东西,并使用全局可变静态这一事实Sender<T>,它代表了我正在尝试实现的同时给出正确的编译器错误.这段代码不打算运行,只是编译.

use std::ops::DerefMut;
use std::sync::{Arc, Mutex};
use std::collections::LinkedList;
use std::sync::mpsc::{Sender, Receiver};

type SafeList = Arc<Mutex<LinkedList<u8>>>;
type SendableFn = Arc<Mutex<(Fn() + Send + Sync + 'static)>>;
static mut tx: *mut Sender<SendableFn> = 0 as *mut Sender<SendableFn>;

fn main() {
    let list: SafeList = Arc::new(Mutex::new(LinkedList::new()));
    loop {
        let t_list = list.clone();
        run(move || {
            foo(t_list.clone());
        });
    }
}

fn run<T: Fn() + Send + Sync + 'static>(task: T) {
    unsafe {
        let _ = (*tx).send(Arc::new(Mutex::new(task)));
    }
}

#[allow(dead_code)]
fn execute(rx: Receiver<SendableFn>) {
    for t in rx.iter() {
        let mut guard = t.lock().unwrap();
        let task = guard.deref_mut();
        task();
    }
}

#[allow(unused_variables)]
fn foo(list: SafeList) { }
Run Code Online (Sandbox Code Playgroud)

有没有更好的方法来解决这个错误和/或我应该通过渠道发送功能的另一种方式?

oli*_*obk 18

问题Fn()是你可以多次调用它.如果您移出捕获的值,则下次调用时该值将不再可用.你需要一个FnOnce()确保调用闭包也移出它,所以它已经消失了,不能再被调用.

没有办法了Arc<Mutex<(FnOnce() + Send + Sync + 'static)>>.这将再次要求您静态保证在调用该函数后,没有其他人可以再次调用它.你不能,因为别人可能有另一个Arc指向你的FnOnce.您可以做的是将其装箱并发送Box<FnOnce() + Send + Sync + 'static>.只有一个拥有者Box.

麻烦的FnOnce()是,你不能真正地调用它Box,因为这需要将它移出Box并调用它.但是我们不知道它的大小,所以我们无法将其移出Box.将来,Box<FnOnce()>闭包可能会直接使用.

"幸运的是"这个问题经常发生,所以有FnBox.可悲的是,这需要每晚工作.此外,我无法弄清楚如何使用在该文档中描述的功能调用语法,但你可以手动调用call_boxBox<FnBox()>.在游乐场试试吧

  • 你可以使用黑客。仍然使用 `Fn` 并且只移入你用 `take` 移出的 `Option`。然后,当您误用 `Fn` 时,会出现运行时错误 (3认同)
  • @JeroenBollen 黑客仍然适用。`移动|| (foo_option.take().unwrap())()` (2认同)