rust

Prevent value from moving after function call


I have a rust program that I am creating that repeatedly runs a function in a thread, which causes a value moved error.

Here's some example code (pretty much the same as mine but simplified to get to the point and with a few names changed around)

use std::{thread}


struct Foo {
    bar: bool
}

impl Foo {
    fn new() -> Self {
        Foo { bar: false };
    }

    fn do_something2(self) {
        // do something

        // technically could be simplified here to self.bar = !some_condition, but someone will           
        // probably complain about it, not really relevant to the issue anyways
        if some_condition {
            self.bar = false;
        }
    }
}


fn do_something(mut foo: Foo) {
    foo.bar = true;

    thread::spawn(|| {
        while foo.bar {
           foo.do_something2();
        }
    });
}


fn main() {
    let mut foo = Foo::new();
    do_something(&mut foo);
    // other code
}

I am not sure how I would stop the variable from being moved. In this example, it could technically be avoided by implementing the Copy trait, but my struct has a Vec as one of the values, so I cannot use Copy and need to find a different way.


Solution

  • First you'll want do_something to take a reference rather than an owned value. You can do that like so:

    fn do_something(foo: &mut Foo) { ... }
    

    The method Foo::doo_something2 should also be changed to take a mutable reference:

    fn do_something2(&mut self) { ... }
    

    Once you do that you'll encounter a new error. thread::spawn has no way to prove that the reference outlives the thread that is being created. Lucky for you there is a new feature in the standard library called "scoped threads" that allows you to prove to the compiler that foo won't be dropped before the child thread terminates.

    You can use scoped threads like so:

    fn do_something(foo: &mut Foo) {
        foo.bar = true;
    
        thread::scope(|scope| {
            scope.spawn(|| {
                while foo.bar {
                    foo.do_something2();
                }
            });
        });
    }