1

In my project I need to do something like:

use std::thread;
use std::time::Duration;

struct A {
    pub ints: Vec<u8>,
}

impl A {
    fn new() -> A {
        let mut a = A {
            ints: vec![1, 5, 6, 2, 3, 4],
        };
        a.timer();
        a
    }

    fn timer(&mut self) {
        thread::spawn(move || {
            loop {
                thread::sleep(Duration::from_millis(1));
                self.ints.remove(0);
            }
        });
    }
}

fn main() {
    let a = A::new();
    loop {
        println!("Remaining elements: {:?}", a.ints);
    }
}

The idea is that some struct contains a vector of elements. These elements should be removed from the vector after some period of time. Think of it as a periodic timer that checks something and performs an action on mutable object (removes an element). This thread also needs to be dropped if the object on which it is working on is deleted. So I guess it should be a member of a struct of which it is manipulating.

The problem with the code above is that it has a lot of borrow errors and I don't understand how to do that.

I have seen few questions like this but each of them was about manipulating scalar in a thread. The reason why I can't apply it here is because the thread should be something that is inside A struct and it should call remove on a vector that is a member of that struct.

I guess I should use Arc or something like that but don't really understand how to use it here.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
VP.
  • 15,509
  • 17
  • 91
  • 161

1 Answers1

2

I guess I should use Arc or something like that but don't really understand how to use it here.

Indeed that is the simplest solution:

You can wrap the ints field in an Arc, but then you wouldn't be able to modify the Vec, so you also wrap it in a Mutex:

struct A {
    pub ints: Arc<Mutex<Vec<u8>>>,
}

Then you can clone the Arc to receive a second handle to the same memory.

fn timer(&mut self) {
    let ints = self.ints.clone();
    thread::spawn(move || {
        loop {
            thread::sleep(Duration::from_millis(1));

Instead of directly accessing the Vec, you then need to lock the Mutex, which can fail if another thread panicked while accessing the Mutex.

            ints.lock().unwrap().remove(0);
        }
    });
}
oli_obk
  • 28,729
  • 6
  • 82
  • 98
  • One more question - will this spawned thread be automatically dropped if `A`-object's lifetime has come to end? – VP. May 25 '16 at 12:23
  • 1
    no, that thread runs forever until it panics. If you want it dropped together with `A`, you need to store the `JoinHandle` returned by the `spawn` function inside the object and implement `Drop` for `A` and call `join` on the handle. – oli_obk May 25 '16 at 12:36
  • Here is also a question - how can I modify the objects in such vector? For example, if it is a vector of some struct, increment a field inside this structure? All the ways I tried has errors about "can't move out of borrowed content" or "the trait `core::iter::Iterator` is not implemented for the type `&std::sync::mutex::MutexGuard<'_, collections::vec::Vec>`". Suppose `Player` as some struct which has a field which I need to change in this thread. – VP. May 25 '16 at 13:24
  • you need to store the iterator in a local variable and access the inner `Vec` by dereferencing the local variable – oli_obk May 25 '16 at 13:27