I'm trying to write a program that spawns a background thread that continuously inserts data into some collection. At the same time, I want to keep getting input from stdin
and check if that input is in the collection the thread is operating on.
Here is a boiled down example:
use std::collections::HashSet;
use std::thread;
fn main() {
let mut set: HashSet<String> = HashSet::new();
thread::spawn(move || {
loop {
set.insert("foo".to_string());
}
});
loop {
let input: String = get_input_from_stdin();
if set.contains(&input) {
// Do something...
}
}
}
fn get_input_from_stdin() -> String {
String::new()
}
However this doesn't work because of ownership stuff.
I'm still new to Rust but this seems like something that should be possible. I just can't find the right combination of Arc
s, Rc
s, Mutex
es, etc. to wrap my data in.
First of all, please read Need holistic explanation about Rust's cell and reference counted types.
There are two problems to solve here:
To share ownership, the simplest solution is Arc
. It requires its argument to be Sync
(accessible safely from multiple threads) which can be achieved for any Send
type by wrapping it inside a Mutex
or RwLock
.
To safely get aliasing in the presence of mutability, both Mutex
and RwLock
will work. If you had multiple readers, RwLock
might have an extra performance edge. Since you have a single reader there's no point: let's use the simple Mutex
.
And therefore, your type is: Arc<Mutex<HashSet<String>>>
.
The next trick is passing the value to the closure to run in another thread. The value is moved, and therefore you need to first make a clone of the Arc
and then pass the clone, otherwise you've moved your original and cannot access it any longer.
Finally, accessing the data requires going through the borrows and locks...
use std::sync::{Arc, Mutex};
fn main() {
let set = Arc::new(Mutex::new(HashSet::new()));
let clone = set.clone();
thread::spawn(move || {
loop {
clone.lock().unwrap().insert("foo".to_string());
}
});
loop {
let input: String = get_input_from_stdin();
if set.lock().unwrap().contains(&input) {
// Do something...
}
}
}
The call to unwrap
is there because Mutex::lock
returns a Result
; it may be impossible to lock the Mutex
if it is poisoned, which means a panic occurred while it was locked and therefore its content is possibly garbage.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With