Using thread loop inside a struct

I need to have a structure with a list, which I can dynamically increase and same time a background thread which will process items from the list and removes processed items from the list. I came up this approach, but maybe there is a better way.
The issue I have is that I can't spawn a thread from start_thread function and provide self as an argument

This is a simplified version:

struct abc_struct {
  data: VecDeque<String>
}

impl abs_struct {
  fn new() -> abc_struct { ... }
  // add new data
  fn append(&mut self, new_data: &str) {
    self.data.push_front(new_data);
  }
  // I want to have an infinite loop to print values from self.data
  fn start_thread(&self) {
        thread::spawn(|| {
           self.print_data()
        });
  }
  // prints all members of self.data in loop
  fn print_data(&self) {
    loop {...}
  }
}


fn main() {
   let mut abc = abc_struct::new();
   // loop in a background to print self.data whenever they will be something...
   abc.start();

   // add data
   abc.append("1");
   abc.append("32eesd");
   // etc.
}

P.S. I'm new in Rust, so please recommend me if I'm approaching things from a wrong side and there is a better way to do it, I'm more than happy to refactor the code, I prefer simple code

One of the things that Rust promises to protect you against is data races, namely, when data is being modified from one thread in parallel with another thread using that data. That's pretty much exactly what you are trying to do here.

One way to do this is to use a Mutex. A Mutex works by introducing a lock method for accessing your data. Whenever you call lock, the mutex will wait until nobody else is accessing the data, and then give you exclusive access to the data. Once the lock goes out of scope, the lock is released and some other call to lock will be given access, if any. That looks like this:

use std::collections::VecDeque;
use std::sync::{Arc, Mutex};
use std::thread;

#[derive(Clone)]
struct AbcStruct {
    data: Arc<Mutex<VecDeque<String>>>,
}

impl AbcStruct {
    fn new() -> AbcStruct {
        AbcStruct {
            data: Arc::new(Mutex::new(VecDeque::new()))
        }
    }
    
    fn append(&self, new_data: String) {
        let mut lock = self.data.lock().unwrap();
        lock.push_front(new_data);
    }
    
    fn start_thread(&self) {
        let me = self.clone();
        thread::spawn(move || me.print_data());
    }
    
    fn print_data(&self) {
        loop {
            let lock = self.data.lock().unwrap();
            println!("{:?}", &*lock);
        }
    }
}

fn main() {
    let abc = AbcStruct::new();
    abc.start_thread();

    for i in 0..1000 {
        abc.append(format!("{}", i));
    }
}

You can try it out here

Note that the above code also uses an Arc to share the data. The way an Arc works is that every time you clone it, you get a new shared handle to the same inner object. The shared object is destroyed when the last clone of the Arc is. This is really useful for sharing between multiple threads.

Note that in the above snippet, the background thread is killed when main returns.

1 Like

Thanks for detailed explanation, Alice.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.