Rust equivalent of concurrent code written in Go?

188 Views Asked by At

I'm stuck at doing multithread feature in Rust. I'm trying to do translate my code written in Go that updates a map's value while iterating and make a new thread. (simplified codes)

my_map := make(map[string]string)
var wg sync.WaitGroup
wg.Add(len(my_map ))

for key := range my_map {
    go func(key string) {
        defer wg.Done()
        stdout, _ := exec.Command(key, "some command").Output()

        lock.Lock()
        defer lock.Unlock()
        my_map[key] = "updating map value while iterating"  // eg stdout
    }(key)
}

I tried so far like this

let mut my_map = HashMap::new();

...

for (key, value) in my_map.iter_mut() {
    // Should update map value, tried with crossbeam_utils:thread;

    thread::scope(|s| {
        s.spawn(|_| {
            let cmd = format!("some command {key}");  // will borrow key
            let cmd: Vec<&str> = cmd.as_str().split(" ").map(|s| s).collect();

            let proc = Popen::create(
                &cmd,
                PopenConfig {
                    stdout: Redirection::Pipe,
                    stdin: Redirection::None,
                    stderr: Redirection::None,
                    ..Default::default()
                },
            );

            let mut proc = proc.expect("Failed to create process.");

            let (out, _) = proc.communicate(None).unwrap();
            let stdout = out.unwrap();

            *value = "I will update value with something new";
        });
    })
    .unwrap();
}
1

There are 1 best solutions below

1
Brandon Kauffman On

I create a Vector of handlers which would be similar to a wait group, and I push each thread there. I then iterate over each handle and .unwrap() it to make sure it finished.

    let mut handles: Vec<std::thread::JoinHandle<()>> = Vec::new();

    for (key, value) in my_map.iter_mut() {
        handles.push(thread::spawn(move || {
            let cmd = format!("some command");
            let cmd: Vec<&str> = cmd.as_str().split(" ").map(|s| s).collect();

            let proc = Popen::create(
                &cmd,
                PopenConfig {
                    stdout: Redirection::Pipe,
                    stdin: Redirection::None,
                    stderr: Redirection::None,
                    ..Default::default()
                },
            );

            let mut proc = match proc {
                Ok(proc) => proc,
                Err(e) => {
                    panic!("Failed to create process: {:?}", e)
                }
            };

            let (out, _) = proc.communicate(None).unwrap();
            let stdout = out.unwrap();

            *value = "I will update value with something new";
        }));
   }
    for handle in handles {
        handle.join().unwrap(); // This won't block other threads, but it will make sure it can unwrap every thread before it continues. In other words, this only runs as long as your longest running thread.
    }