I want to return an error from a function in case a condition is true:
use std::error::Error;
pub fn run() -> Result<(), Box> {
//
A Result<T, E>
is an enum with two variants. To return either of them, you just use corresponding variants.
fn foo(var: bool) -> Result<(), i32> {
if var {
Ok(()) //in fact this is std::result::Result::Ok
} else {
Err(-3) //in fact this is std::result::Result::Err
}
}
The reason why you don't have to write std::result::Result::Ok
is that it is in the prelude. As you can see, you don't have to stick to Box<Error>
, but can return any type you want. It is a generic enum, with no restrictions.
The ?
-operator is a handy shortcut for early returns, so you don't have to be too verbose about results.
Box<dyn Error>
is handy for types that implement it:
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
Err("March")?
}
but surprisingly, it doesnt work with all types:
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
// the trait `std::error::Error` is not implemented for `{integer}`
Err(9)?
}
as a workaround, you can use what I call the Error Format idiom:
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
Err(format!("{}", 9))?
}
Note this has many variations, for example with literals you can do this:
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
Err(concat!(9))?
}
and also, you may not even need to use Box<dyn Error>
:
fn main() -> Result<(), String> {
Err(concat!(9))?
}
It can also be useful in cases where you normally dont need it. For example, this example below could work without it, but its useful as it adds the filename to the error, which normally isnt shown:
use std::fs;
fn main() -> Result<(), String> {
let s = "a.rs";
match fs::read_to_string(s) {
Ok(v) => print!("{}", v),
// normal message is just: The system cannot find the file specified
Err(v) => Err(format!("{} {}", s, v))?
}
Ok(())
}
I am new to Rust, but here is my dirty hack to return custom errors, given that the function is set to return Result<(), Box<dyn Error>>
:
fn serve(config: &Config, stream: TcpStream) -> Result<(), Box<dyn Error>> {
// ...
if request_is_bad() {
// This returns immediately a custom "Bad request" error
Err("Bad request")?;
}
// ...
}
Error
is a trait and you want to return a trait object (note the dyn keyword), so you need to implement this trait:
use std::error::Error;
use std::fmt;
#[derive(Debug)]
struct MyError(String);
impl fmt::Display for MyError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "There is an error: {}", self.0)
}
}
impl Error for MyError {}
pub fn run() -> Result<(), Box<dyn Error>> {
let condition = true;
if condition {
return Err(Box::new(MyError("Oops".into())));
}
Ok(())
}
fn main() {
if let Err(e) = run() {
println!("{}", e); // "There is an error: Oops"
}
}
Debug
, Display
, then Error
for it,Err
variant of Result
.I advise you to use failure that remove all the error boilerplate:
#[derive(Fail, Debug)]
#[fail(display = "There is an error: {}.", _0)]
struct MyError(String);
--
Note that if you expect an Error
, you can return whatever type you want, given that it implements Error
. This includes the error types in std
.