【问题标题】:Cannot move out of captured outer variable in an `Fn` closure无法移出“Fn”闭包中捕获的外部变量
【发布时间】:2016-02-13 05:25:08
【问题描述】:

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

error: cannot move out of captured outer variable in an 'Fn' closure

忽略此代码完全不执行任何操作并使用全局可变静态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) { }

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

【问题讨论】:

    标签: rust function-pointers channel


    【解决方案1】:

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

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

    FnOnce() 的问题在于,当它位于 Box 中时,您无法真正调用它,因为这需要将其移出 Box 并调用它。但是我们不知道它的大小,所以我们不能把它移出Box。将来Box&lt;FnOnce()&gt; 闭包可能会直接可用。

    “幸运的是”这个问题出现的频率更高,所以有FnBox。可悲的是,这需要每晚工作。我也不知道如何使用文档中描述的函数调用语法,但您可以在Box&lt;FnBox()&gt; 上手动调用call_box。在Playground试试吧

    【讨论】:

    • 不幸的是,稳定是我唯一的选择。看起来您刚刚确认了我已经尝试并弄清楚的内容,但其他人可能会发现这个能够在夜间使用的超级有用的东西。谢谢:)
    • 您可以使用 hack。仍然使用Fn,并且只使用take 移出的Options。然后,当您误用 Fn 时会出现运行时错误
    • 如果我需要 Fn 怎么办,因为图书馆的要求。
    • @JeroenBollen 黑客仍然适用。 move || (foo_option.take().unwrap())()
    猜你喜欢
    • 2020-01-16
    • 2016-12-24
    • 2019-04-01
    • 2015-08-14
    • 2019-10-14
    • 1970-01-01
    • 1970-01-01
    • 2022-11-19
    相关资源
    最近更新 更多