How to allow a BTreeMap into a closure that ends execution

I have a very simple program that gathers data and fills a BTreeMap. I want the program to print its gathered data when the program is terminated by the user (ctrl-c). I got the concept down, but the move into the closure of the ctrl-c handler refuses the compiler from compiling. How can I make this working?

use std::collections::BTreeMap;
use ctrlc;
use std::thread;
use std::time::{Duration, Instant};
use std::process;

fn main() {

    let mut threads: BTreeMap<String, u64> = BTreeMap::new();

    ctrlc::set_handler(move || {
        println!("{:#?}", &threads);
        process::exit(0);
    }).expect("Error setting ctrl-c handler");

    loop {
        let wait_time_ms = Duration::from_millis(100);
        let start_time = Instant::now();
        //perform_threads_snapshot(&hostname_vec, &port_vec, 1, &mut threads );
        let counter = threads.entry(String::from("A")).or_insert(0);
        *counter+=1;
        thread::sleep( wait_time_ms-start_time.elapsed() );
    }

}

(add ctrlc = "3.2.1" to your Cargo.toml)

The error message is:

error[E0382]: borrow of moved value: `threads`
  --> src/main.rs:20:23
   |
9  |     let mut threads: BTreeMap<String, u64> = BTreeMap::new();
   |         ----------- move occurs because `threads` has type `BTreeMap<String, u64>`, which does not implement the `Copy` trait
10 |
11 |     ctrlc::set_handler(move || {
   |                        ------- value moved into closure here
12 |         println!("{:#?}", &threads);
   |                            ------- variable moved due to use in closure
...
20 |         let counter = threads.entry(String::from("A")).or_insert(0);
   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ value borrowed here after move

Which totally makes sense to me, however, in this case the program is terminated upon handler invocation.

The underlying problem here is that the Ctrl-C handler might happen in the middle of an insert, so threads might be in a temporarily inconsistent state. You'll need to synchronize these accesses somehow:

(untested)

use std::collections::BTreeMap;
use ctrlc;
use std::thread;
use std::time::{Duration, Instant};
use std::process;
use std::sync::{Arc,Mutex};

fn main() {

    let threads: Arc<Mutex<BTreeMap<String, u64>>> = Default::default();

    {
        let threads = Arc::clone(&threads);
        ctrlc::set_handler(move || {
            println!("{:#?}", &*(threads.lock().unwrap()));
            process::exit(0);
        }).expect("Error setting ctrl-c handler");
    }

    loop {
        let wait_time_ms = Duration::from_millis(100);
        let start_time = Instant::now();
        //perform_threads_snapshot(&hostname_vec, &port_vec, 1, &mut threads );
        {
            let threads = threads.lock.unwrap();
            let counter = threads.entry(String::from("A")).or_insert(0);
            *counter+=1;
        }
        thread::sleep( wait_time_ms-start_time.elapsed() );
    }
}
3 Likes

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.