Defining such things is currently not particularly neat; There are a few things you need to configure with a custom error type, but after you have done this, it will be much easier for you.
First of all, you'll want to implement std::error::Error for a CmdError (which requires std::fmt::Display and std::fmt::Debug ), and then to try! could work automatically, std::convert::From<std::string::FromUtf8Error> and std::convert::From<std::io::Error> , Here are the implementations of these:
use std::error::Error; use std::string::FromUtf8Error; use std::fmt; use std::io;
(The description method in the Error implementation may return a string that is not based on a completed error, for example, βthe command could not be started.β If you need details, they will still be in Error.cause() .)
After the implementation of this batch, everything is much simpler, because we can use try! . run_cmd can be written like this:
fn run_cmd(cmd: &str) -> Result<String, CmdError> { let output = try!(Command::new("sh").arg("-c").arg(cmd).output()); Ok(try!(String::from_utf8(output.stdout))) }
Since try! uses the From infrastructure, all this is much simpler; the first line can return Err(CmdError::IoError(_)) (for Command.output() returns Result<_, io::Error> ), and the second line can return Err(CmdError::UtfError(_)) (for String::from_utf8(β¦) returns Result<_, FromUtf8Error> ).
Your main may also be somewhat simpler, and the err branch does not need further coordination if you do not care about a specific error; since it now implements fmt::Display , you can just use it directly.
By the way, in the format string {:} should be written as {} ; : is redundant if you do not follow anything. ( {:?} will work for Debug output, but you should use Display if it is facing the user.)