枚举中的FnOnce: 无法移出借用内容。

5

我刚开始学习Rust,对于所有权和借用还有些困惑。在这种情况下,我想将FnOnce存储在枚举中,然后在另一个线程中稍后调用它。我尝试了很多不同的变体,但总是卡在某个地方。这是我目前拥有的一个简化版本:

#![feature(fnbox)]

use std::sync::{Arc, Mutex};
use std::boxed::{Box, FnBox};

enum Foo<T> {
    DoNothing,
    CallFunction(Box<FnBox(&T) + Send>)
}

struct FooInMutex<T> {
    foo: Arc<Mutex<Foo<T>>>
}

impl<T> FooInMutex<T> {
    fn put_fn(&self, f: Box<FnBox(&T)+Send>) {
        let mut foo = self.foo.lock().unwrap();
        let mut new_foo : Foo<T>;
        match *foo {
            Foo::DoNothing =>
                new_foo = Foo::CallFunction(f),
            _ =>
                new_foo = Foo::DoNothing
        }
        *foo = new_foo;
    }

    fn do_it(&self, t: T) {
        let mut foo = self.foo.lock().unwrap();
        let mut new_foo : Foo<T>;
        match *foo {
            Foo::CallFunction(ref mut f) => {
                //f(&t)
                f.call_box((&t,));
                new_foo = Foo::DoNothing;
            }
            _ =>
                panic!("...")
        }
        *foo = new_foo;
    }
}

#[test]
fn it_works() {
    let x = FooInMutex { foo: Arch::new(Mutex::new(Foo::DoNothing)) };

    x.put_fn(Box::new(|| panic!("foo"))); 
    x.do_it();
}

我使用的是 "rustc 1.4.0-nightly (e35fd7481 2015-08-17)"。 错误信息:
src/lib.rs:35:17: 35:18 error: cannot move out of borrowed content
src/lib.rs:35                 f.call_box((&t,));
                              ^

据我理解,在互斥体中,f是由枚举所拥有的,而我只通过*foo借用它。但是,为了调用f,我需要将其移出。但是如何做到这一点?或者我需要做什么其他修改才能使这个示例工作?
1个回答

7

std::mem::replace 是你应该在那里使用的函数,像这样:

use std::mem;

…

    fn do_it(&self, t: T) {
        match mem::replace(self.foo.lock().unwrap(), Foo::DoNothing) {
            Foo::CallFunction(f) => {
                f.call_box((&t,));
            }
            _ => panic!("...")
        }
    }

文档参考:std::mem::replace - Christopher Stevenson
你能解释一下为什么它一开始没有起作用吗?为什么需要将 f 移出来? - tafia
@tafia:FnBox 基于 FnOnce,它消耗闭包环境;也就是说,f.call_box 通过值获取 self,因此 f 必须是 Box<FnBox(&T) + Send>。对其进行可变引用是不够的,在您最初的示例中只有可变引用(因此出现“无法从借用的上下文中移动 Box<FnBox(&T) + Send>` 的情况)。 - Chris Morgan
@Chris Morgan:谢谢!这个回答值得让下一个读者看到。 - tafia

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接