Rust: как передать hashmap между thread?

Только начал работать с тредами в Rust. Не могу передать хэштаблицу между двумя тредами. Как правильно это делать и где моя ошибка ? Задача - собирать хэштаблицу в одном треде и печатать в другом.

use std::thread;
use std::time::Duration;
use std::fs;
use std::io::{stdin, Read};
use std::collections::HashMap;


fn main() {
    
    let mut hello = String::from("Hello, ");
    let mut influx_data:HashMap<String, i32> = HashMap::new();

//second thread
    thread::spawn(move || {
        let mut character = [0];
        hello = String::from("sasasas ");
        while let Ok(_) = stdin().read(&mut character) {
            
if character[0] == b'\n' {
        influx_data.entry(hello).and_modify(|count| *count += 1).or_insert(1);  
            println!("counter_bss cnt=7\n");
            }
        }

            thread::sleep(Duration::from_secs(1));
    });

//main thread    
    loop {
        let data = "Some data!";
        fs::write("/tmp/foo", data).expect("Unable to write file");

        for (key, value) in &influx_data {
        println!("{} {}", key, value);
        }

        thread::sleep(Duration::from_secs(10));
   }

}

Ошибка

   |
23 |                 influx_data.entry(hello).and_modify(|count| *count += 1).or_insert(1);  
   |                                   ^^^^^ value moved here, in previous iteration of loop

Ответы (1 шт):

Автор решения: Pak Uula

Перво-наперво, у вас проблема не с мапой, а с ключом hello. Rust беспокоится, что символ \n может встретиться несколько раз, а entry дропнет ключ после первого раза. Вам нужно вызывать entry(hello.clone())

Далее, у вас один объект HashMap, но писатели и читатели находятся в разных потоках. У них разное время жизни, не вложенное друг в друга. Поэтому вы не можете сделать просто ссылку и передать её в функцию spawn, получите ошибку

`influx_data` does not live long enough
borrowed value does not live long enough

Вам нужно сделать shared container. Специально для многопоточного доступа в Rust есть Mutex и Arc. Комбинация этих контейнеров даёт общий доступ к объектам из разных потоков

let data_owner = Arc::new(Mutex::new(influx_data));
let data_for_thread = data_owner.clone();

Контейнер data_owner хранит вашу мапу для main, контейнер data_for_thread переедет в замыкание. Для доступа к мапе нужно захватывать мутекс:

data_for_thread.lock().unwrap().entry(...)
...
for (key, value) in data_owner.lock().unwrap().iter() {...}
use std::collections::HashMap;
use std::fs;
use std::io::{stdin, Read};
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;

fn main() {
    let mut hello = String::from("Hello, ");
    let influx_data: HashMap<String, i32> = HashMap::new();
    let data_owner = Arc::new(Mutex::new(influx_data));
    let data_for_thread = data_owner.clone();

    //second thread
    thread::spawn(move || {
        let mut character = [0];
        hello = String::from("sasasas ");
        while let Ok(_) = stdin().read(&mut character) {
            if character[0] == b'\n' {
                data_for_thread
                    .lock()
                    .unwrap()
                    .entry(hello.clone())
                    .and_modify(|count| *count += 1)
                    .or_insert(1);
                println!("counter_bss cnt=7\n");
            }
        }

        thread::sleep(Duration::from_secs(1));
    });

    //main thread
    loop {
        let data = "Some data!";
        fs::write("/tmp/foo", data).expect("Unable to write file");

        for (key, value) in data_owner.lock().unwrap().iter() {
            println!("{} {}", key, value);
        }

        thread::sleep(Duration::from_secs(10));
    }
}

Лог получается такой:

1
counter_bss cnt=7

2
counter_bss cnt=7

3
counter_bss cnt=7

sasasas  3
4
counter_bss cnt=7

sasasas  4
5
counter_bss cnt=7

sasasas  5

Работает

→ Ссылка