Skip to content

Instantly share code, notes, and snippets.

@Technius
Created April 22, 2019 23:57
Show Gist options
  • Save Technius/43977937a28e8846d917b53605e32cc3 to your computer and use it in GitHub Desktop.
Save Technius/43977937a28e8846d917b53605e32cc3 to your computer and use it in GitHub Desktop.
Asynchronous process I/O in Rust using tokio
// This code sample needs the dependencies:
// tokio = "0.1"
// tokio-process = "0.2"
use tokio::prelude::*;
use std::process::{Command, Stdio};
use tokio_process::CommandExt;
use std::time::Duration;
fn main() {
// We'll start our process here.
let mut child_process =
Command::new("sleep")
.arg("10")
.stdout(Stdio::piped())
// tokio-process extends Command with several methods that will spawn
// the process asynchronously. Here, it will spawn a
// tokio_process::Child, which supports asynchronous I/O.
.spawn_async()
.expect("Could not spawn process");
let out = child_process.stdout() // Let's try to get the stdout handle.
// This is a &mut Option, so remove the Option value
.take()
// and unwrap the ChildStdout
.unwrap();
// This is how long we're willing to wait for the child process to respond.
// We'll be using this later.
let timeout_length = Duration::from_secs(5);
// Now, we want to read from our process.
// A tokio::codec::Codec can be used to decode the process output into a
// type that we want. Here, we'll use a LinesCodec that will transform the
// output bytes into lines of text.
let lines_codec = tokio::codec::LinesCodec::new();
// Next, we'll create a Future (a description of an asynchronous
// computation) that will represent the output (or lack thereof) of our
// child process.
let line_fut =
// We'll read the process output using our codec, producing a Stream
tokio::codec::FramedRead::new(out, lines_codec)
// We only want the first line, so we'll only take that from the Stream
.take(1)
// Gather the stream elements (in this case, just a line) into a Future
// of a Vec
.collect()
// Then, we'll try to take the first item from the results.
.map(|lines| lines.first().cloned())
// We'll use the timeout length we've defined above to ensure our Future
// fails if the process doesn't respond
.timeout(timeout_length);
// Now, we'll execute our computation. Here, I'll demonstrate how you can
// get the results back on your main thread. If you don't need to, you can
// use tokio::run to execute your Future, which will basically do the same
// as what will follow anyways.
// We will need a tokio Runtime that can execute our Future. Here's one way
// to obtain one:
let mut runtime = tokio::runtime::Runtime::new().expect("Could not create tokio runtime");
// Finally, we can execute our Future and wait for it to finish.
let result = runtime.block_on(line_fut);
match result {
Ok(Some(line)) => println!("Got output: {}", line),
Ok(None) => println!("No output received"),
Err(err) => println!("Encountered error: {}", err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment