How to manually return a Result<(), Box<dyn Error>>?

20,401

Solution 1

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"
    }
}
  • Create your own error type,
  • Implement Debug, Display, then Error for it,
  • If there is an error, return the 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.

Solution 2

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")?;
    }
    // ...
}

Solution 3

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 doesn't 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 don't need it. For example, this example below could work without it, but it's useful as it adds the filename to the error, which normally isn't 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(())
}
Share:
20,401

Related videos on Youtube

rect0x51
Author by

rect0x51

Interested in language design and implementation. My though process is the following: A general-purpose language should provide as much "freedom" as possible to the programmer. Instead of artificially constraining GPLs to make programming simpler, DSLs should be used. Thus, a GPL should mostly be a metaprogramming language. I am dedicated to helping the Nim language evolve, as I believe it has the best shot in that direction. Other than CS, I am a big fan and advanced player of Shogi, a board game.

Updated on November 02, 2021

Comments

  • rect0x51
    rect0x51 over 2 years

    I want to return an error from a function in case a condition is true:

    use std::error::Error;
    
    pub fn run() -> Result<(), Box<dyn Error>> {
        // -- snip ---
    
        if condition {
            // return error
        }
    
        // -- snip --
    
        Ok(())
    }
    
    fn main() {}
    

    I probably don't have the basics of the typesystem down, but everywhere I looked people use the ? operator, so I can't figure out what type to return.

    1. Is it possible to just return an error like this?
    2. Is there a better way to handle this logic?
  • rect0x51
    rect0x51 almost 6 years
    Wow, that's a lot... Now I have even more questions... 1. So it's mandatory to create my own error type? Is it not possible to return an error that's already defined in std? 2. How would the code look if I use the compiler attributes that you suggested? 3. what is this dyn??? It's not even listed on Appendix - A of "The Rust Programming Language"!
  • Boiethios
    Boiethios almost 6 years
    @ΣτέφανοςΜανδαλάς I cannot answer right now. In 1 hour or so.
  • rect0x51
    rect0x51 almost 6 years
    I'm ok with the very basics. In this case though I needed a function that returns a generic error, so I can use ? operator for returning error in case a file can't open etc but also be able to return errors manually (which seems not possible with the ? operator).
  • Boiethios
    Boiethios almost 6 years
    @ΣτέφανοςΜανδαλάς I updated my answer about your points 1 and 3. The attributes generate the boilerplate code that you are afraid of ;) Furthermore, IIRC, the Fail trait permit more usefull things, but you can read the doc to know more.
  • Stargateur
    Stargateur about 5 years
    Err("Bad request")?
  • jimis
    jimis about 5 years
    return Err("Bad request") was the first thing I tried. Unfortunately the compiler complains: "expected struct std::boxed::Box, found reference"
  • Shepmaster
    Shepmaster about 5 years
    return Err("Bad request".into());
  • jimis
    jimis about 5 years
    Works! Also I now see what @Stargateur meant, because just writing Err("Bad request")? (without return statement and with question mark at the end) also works! I think both of these solutions are much more elegant. Should I edit the answer?
  • perrohunter
    perrohunter almost 5 years
    @FrenchBoiethios Can you somehow check for the type MyError instead of just printing the error? I'm trying to use this example to return different error types, but compiler says it's always Box<Error>
  • Boiethios
    Boiethios almost 5 years
    You can downcast your error: play.rust-lang.org/…
  • Svilen
    Svilen over 4 years
    Could someone explain why appending ? or using .into() work as a solution? Is one preferred over the other? @Stargateur @Shepmaster
  • Hutch Moore
    Hutch Moore almost 4 years
    @Svilen I don't know about other people, but I prefer only using ? when it doesn't always return. Using a literal Err always returns, of course.
  • Tuupertunut
    Tuupertunut over 2 years
    @Svilen From<&str> is implemented for Box<dyn Error> and both .into() and ? are converting strings to error boxes using that implementation. (see doc.rust-lang.org/std/boxed/…)