Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

FnOnce inside Enum: cannot move out of borrowed content

Tags:

rust

I'm new to rust and still have some problems with ownership/borrowing. In this case, I want to store a FnOnce in an enum and then call it later on from another thread. I tried a lot of different variants but always get stuck somewhere. Here is a reduced variant of what I currently have:

#![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();
}

I use "rustc 1.4.0-nightly (e35fd7481 2015-08-17)". The error message:

src/lib.rs:35:17: 35:18 error: cannot move out of borrowed content
src/lib.rs:35                 f.call_box((&t,));
                              ^

As I understand it, f is owned by the enum in the mutex and I only borrow it via *foo. But for calling f, I need move it out. But how to do this? Or what else do I have to change to make this example work?

like image 895
Florianx Avatar asked Aug 26 '15 20:08

Florianx


1 Answers

std::mem::replace is what you should use there, like this:

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!("...")
        }
    }
like image 132
Chris Morgan Avatar answered Nov 15 '22 08:11

Chris Morgan