Reputation: 1437
I try to write a function that takes an io::Write
as an argument in order to output binary data. The tricky part is now, that the function internally uses a thread to produce that data (I know in the following code the usage of a thread does not make sense - it's there for demonstration).
Currently, I've different approaches:
fn process<W>(mut w: W) where W: io::Write + Send + Sync + 'static {
thread::spawn(move || {
write!(w, "hello world").unwrap();
}).join().unwrap();
}
fn main() {
let output = Vec::new();
process(output);
}
The problem here is that output
cannot be used after the call of process
since it moved in there.
fn process<W>(w: &mut W) where W: io::Write + Send + Sync + 'static {
thread::spawn(move || {
write!(w, "hello world").unwrap();
}).join().unwrap();
}
fn main() {
let output = Vec::new();
process(&mut output);
}
This is my preferred signature of process
, but does not compile because the lifetime of w
seem to be shorter (for the compiler) than the lifetime of the thread.
fn process(w: Arc<Mutex<io::Write + Send>>) {
thread::spawn(move || {
let mut w = w.lock().unwrap();
write!(w, "hello world").unwrap();
}).join().unwrap();
}
fn main() {
let buffer = Arc::new(Mutex::new(Vec::new() as Vec<u8>));
process(buffer.clone());
}
This works, but the signature of process
exposes the internal implementation detail that a thread is used. The kind of design I would like to avoid, since in later versions the thread might disappear.
Does someone has a better solution? Many thanks in advance.
Upvotes: 2
Views: 105
Reputation: 16670
You can pass ownership of W to the function, then to the thread, then back to the calling scope.
fn process<W>(mut w: W) -> W where W: io::Write + Send + 'static {
thread::spawn(move || {
write!(w, "hello world").unwrap();
w
}).join().unwrap()
}
fn main() {
let output = Vec::new();
let output = process(output);
let output = process(output);
}
The Sync constraint is unnecessary so I removed it.
Upvotes: 3
Reputation: 128151
Depending on the architecture of your actual program, you may want to use crossbeam
:
extern crate crossbeam;
use std::io;
fn process<W>(w: &mut W) where W: io::Write + Send {
crossbeam::scope(|scope| {
scope.spawn(|| {
write!(w, "hello world").unwrap();
});
});
}
fn main() {
let mut buffer = Vec::new();
process(&mut buffer);
}
With crossbeam, it is possible to spawn threads which may hold to non-static references. It is guaranteed that these threads will outilve the captured references because all threads are implicitly joined when crossbeam::scope()
function exits. Therefore, with crossbeam it is possible both to omit 'static
and Sync
bounds on W
and to use &mut W
reference in the thread closure.
Upvotes: 2