How to manually return a Result<(), Box<dyn Error>>?
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
, thenError
for it, - If there is an error, return the
Err
variant ofResult
.
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(())
}
Related videos on Youtube
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, 2021Comments
-
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.- Is it possible to just return an error like this?
- Is there a better way to handle this logic?
-
rect0x51 almost 6 yearsWow, 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 almost 6 years@ΣτέφανοςΜανδαλάς I cannot answer right now. In 1 hour or so.
-
rect0x51 almost 6 yearsI'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 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 about 5 years
Err("Bad request")?
-
jimis about 5 yearsreturn Err("Bad request") was the first thing I tried. Unfortunately the compiler complains: "expected struct
std::boxed::Box
, found reference" -
Shepmaster about 5 years
return Err("Bad request".into());
-
jimis about 5 yearsWorks! 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 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 alwaysBox<Error>
-
Boiethios almost 5 yearsYou can downcast your error: play.rust-lang.org/…
-
Svilen over 4 yearsCould someone explain why appending
?
or using.into()
work as a solution? Is one preferred over the other? @Stargateur @Shepmaster -
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 over 2 years@Svilen
From<&str>
is implemented forBox<dyn Error>
and both.into()
and?
are converting strings to error boxes using that implementation. (see doc.rust-lang.org/std/boxed/…)