fudgeBreadstein
fudgeBreadstein

Reputation: 97

Proper way to share references to Vec between threads

I am new to rust and I am attempting to create a Vec that will live on the main thread, and pass a reference to another thread, which then pushes members onto the vector, for the main thread to use.

use std::{thread};

fn main() {
    let mut v: Vec<u8> = Vec::new();
    let _ = thread::spawn(move || {
        vec_push(&mut v, 0)
    });
    for i in v.iter_mut() {
        println!("poo {}", i);
    }
}

fn vec_push(v: &mut Vec<u8>, n: u8) {
    v.push(n);
}

This is a simplified version of what I am trying to do. In my main code I am want it to be a Vec of TcpStreams. I think this post would also apply to maintaining a struct (that doesn't implement Copy) between threads. I get this error

error[E0382]: borrow of moved value: `v`
 --> src/main.rs:8:11
  |
4 |     let mut v: Vec<u8> = Vec::new();
  |         ----- move occurs because `v` has type `Vec<u8>`, which does not implement the `Copy` trait
5 |     let _ = thread::spawn(move || {
  |                           ------- value moved into closure here
6 |         vec_push(&mut v, 0)
  |                       - variable moved due to use in closure
7 |     });
8 |     for i in v.iter_mut() {
  |              ^^^^^^^^^^^^ value borrowed here after move

Is there a better way to do this? Am I missing some basic concept? Any help would be useful, I am used to C where I can just throw around references willy-nilly

Upvotes: 0

Views: 1268

Answers (1)

Aleksander Krauze
Aleksander Krauze

Reputation: 6061

What you are doing is wildly unsound. You are trying to have two mutable references to a object, which is strictly forbidden in rust. Rust forbids this to prevent you from having data races that would result in memory unsafety.

If you want to mutate an object from different threads you have to synchronize it somehow. The easiest way to do it is by using Mutex. This probably won't be very efficient in a high-congestion scenario (as locking a mutex can become your bottle neck), but it will be safe.

To share this Mutex between threads you can wrap it in an Arc (an atomic counted shared reference smart pointer). So your code can be transformed to something like this:

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

fn main() {
    let v = Arc::new(Mutex::new(Vec::new()));
    let v_clone = Arc::clone(&v);
    
    let t = thread::spawn(move || {
        vec_push(v_clone, 0)
    });
    t.join().unwrap();

    for i in v.lock().unwrap().iter_mut() {
        println!("poo {}", i);
    }
}

fn vec_push(v: Arc<Mutex<Vec<u8>>>, n: u8) {
    v.lock().unwrap().push(n);
}

You probably will also want to join your spawned thread, so you should name it.

Upvotes: 1

Related Questions