2

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.

  • 1
    `fn do_something(mut foo: Foo)` moves the `foo` into that function. You call it like you want it to be `fn do_something(foo: &mut Foo)` instead. But then you'll have problems with `thread::spawn`. – PitaJ Dec 06 '22 at 17:53
  • 1
    Same with `fn do_something2(self)` which should probably be `fn do_something2(&mut self)` – PitaJ Dec 06 '22 at 17:54
  • 1
    You can look at [how to use references to stack variables in a thread](https://stackoverflow.com/questions/32750829/how-can-i-pass-a-reference-to-a-stack-variable-to-a-thread) to see how you coud prevent the move while still passing it to the thread. – cafce25 Dec 06 '22 at 18:03
  • @cafce25 I added the thread part to show that, like @PitaJ said, there would be conflicts with doing `fn do_something(foo: &mut Foo)`. The thread isn't causing the move, just re-referencing the variable. Sorry about my vague statement at the beginning of the question. I could try doing `fn do_something(&mut Foo)`, but then it will probably take longer to deal with the exceptions the thread causes (unless the thing you linked is an answer to that). – a human being Dec 06 '22 at 18:36
  • I decided to do the thing from @PitaJ's comment, but now I will have to do with the `\`foo\` has an anonymous lifetime \`'_\` but needs to fulfill a \`'static\` lifetime requirement`. Edit: Should I create a new question for this? – a human being Dec 06 '22 at 18:56
  • Are we talking about moving `foo` in `main`? Or in other words is `//other code` using `foo` – cafce25 Dec 06 '22 at 19:18
  • 2
    The issue is that `thread::spawn` spawns a new thread but doesn't wait it to complete, so you can't access `foo` while the thread could potentially still be using it. You need some way to enforce exclusive access to `foo`, potentially a mutex. Why are you spawning threads? Could you provide a bigger picture of what you're trying to do? I'm curious why you chose this approach. – Coder-256 Dec 06 '22 at 19:24
  • Just a note: you can get around that limitation of `thread::spawn` by using `thread::scope` instead. – PitaJ Dec 06 '22 at 19:40
  • @Coder-256 I am going to change something using the main thread, using multithreading so the function can be run simultaneously – a human being Dec 07 '22 at 12:38

1 Answers1

1

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();
            }
        });
    });
}
bddap
  • 529
  • 4
  • 8