Reputation: 1069
I'm struggling with error handling cleanly in Rust. Say I have a function that is propogating multiple error types with Box<dyn Error>
. To unwrap and handle the error, I'm doing the following:
fn main() {
let json =
get_json_response(format!("{}{}", BASE_URL, LOGIN_URL).as_str()).unwrap_or_else(|e| {
eprintln!("Error: failed to get: {}", e);
std::process::exit(1);
});
}
fn get_json_response(url: &str) -> Result<Value, Box<dyn Error>> {
let resp = ureq::get(url)
.set("Authorization", format!("Bearer {}", API_TOKEN).as_str())
.call()?
.into_json()?;
Ok(resp)
}
This works well. However, if I have multiple calls to get_json_response()
, it gets messy to include that same closure over and over.
My solutions is to change it to:
use serde_json::Value;
use std::error::Error;
use ureq;
fn main() {
let json =
get_json_response(format!("{}{}", BASE_URL, LOGIN_URL).as_str()).unwrap_or_else(fail);
}
fn fail(err: Box<dyn Error>) -> ! {
eprintln!("Error: failed to get: {}", err);
std::process::exit(1);
}
This doesn't work because unwrap_or_else()
expects a Value
to be returned rather than nothing !
. I can cheat and change the return value of fail()
to -> Value
and add Value::Null
after the exit(1)
. It works but feels wrong (and complains).
I could also do unwrap_or_else(|e| fail(e))
which isn't terrible.
Is there an idiomatic way to handle this?
Upvotes: 3
Views: 1823
Reputation: 862
I'm not sure if that is idiomatic to Rust, but you can put all the stuff inside a closure and handle the Result
afterwards:
use serde_json::Value;
use std::error::Error;
use ureq;
fn get_json_response(url: &str) -> Result<Value, Box<dyn Error>> {
(|| {
let resp = ureq::get(url)
.set("Authorization", format!("Bearer {}", API_TOKEN).as_str())
.call()?
.into_json()?;
Ok(resp)
})().or_else(|err: Box<dyn Error>| {
eprintln!("Error in get_json_response(): failed to get: {}", err);
std::process::exit(1);
// or just return Err(err) if you don't want to exit
})
}
fn main() {
let json =
get_json_response(format!("{}{}", BASE_URL, LOGIN_URL).as_str()).unwrap();
}
Upvotes: 0
Reputation: 155216
As pointed out by @kmdreko, your code fails to compile because, while !
can be coerced to any T
, fn() -> !
cannot be coerced to fn() -> T
.
To work around the above, you can declare fail()
to return Value
, and actually return the "value" of std::process::exit(1)
. Omitting the semicolon coerces the !
to Value
, and you don't have to cheat with a Value::Null
:
fn main() {
let _json = get_json_response("...").unwrap_or_else(fail).as_str();
}
fn fail(err: Box<dyn Error>) -> Value {
eprintln!("Error: failed to get: {}", err);
std::process::exit(1)
}
Upvotes: 3