Dealing with multiple error conditions in Rust [duplicate] - rust

Is it possible to handle multiple different errors at once instead of individually in Rust without using additional functions? In short: what is the Rust equivalent to a try-catch statement?
A feature like this (First-class error handling with ? and catch) was suggested back in 2016, but I can't tell what came out of it and how a 2019 solution for such a problem might look like.
For example, doing something like this:
try {
do_step_1()?;
do_step_2()?;
do_step_3()?;
// etc
} catch {
alert_user("Failed to perform necessary steps");
}
Instead of:
match do_steps() {
Ok(_) => (),
_ => alert_user("Failed to perform necessary steps")
}
// Additional function:
fn do_steps() -> Result<(), Error>{
do_step_1()?;
do_step_2()?;
do_step_3()?;
// etc
Ok(())
}
My program has a function which checks a variety of different places in the registry for different data values and returns some aggregate data. It would need to use many of these try-cache statements with try-catch inside of other try-catch inside of loops.

There is no try catch statement in Rust. The closest approach is the ? operator.
However, you do not have to create a function and a match statement to resolve it in the end. You can define a closure in your scope and use ? operator inside the closure. Then throws are held in the closure return value and you can catch this wherever you want like following:
fn main() {
let do_steps = || -> Result<(), MyError> {
do_step_1()?;
do_step_2()?;
do_step_3()?;
Ok(())
};
if let Err(_err) = do_steps() {
println!("Failed to perform necessary steps");
}
}
Playground
Is it possible to handle multiple different errors at once instead of individually in Rust without using additional functions?
There is a anyhow crate for the error management in Rust mostly recommended nowadays.
As an alternative, There is a failure crate for the error management in Rust. Using Failure, you can chain, convert, concatenate the errors. After converting the error types to one common type, you can catch (handle) it easily.

Results in Rust can be chained using and_then. So you can do this:
if let Err(e) = do_step_1().and_then(do_step_2).and_then(do_step_3) {
println!("Failed to perform necessary steps");
}
or if you want a more compact syntax, you can do it with a macro:
macro_rules! attempt { // `try` is a reserved keyword
(#recurse ($a:expr) { } catch ($e:ident) $b:block) => {
if let Err ($e) = $a $b
};
(#recurse ($a:expr) { $e:expr; $($tail:tt)* } $($handler:tt)*) => {
attempt!{#recurse ($a.and_then (|_| $e)) { $($tail)* } $($handler)*}
};
({ $e:expr; $($tail:tt)* } $($handler:tt)*) => {
attempt!{#recurse ($e) { $($tail)* } $($handler)* }
};
}
attempt!{{
do_step1();
do_step2();
do_step3();
} catch (e) {
println!("Failed to perform necessary steps: {}", e);
}}
playground

There's also an unstable feature called try_blocks (https://doc.rust-lang.org/beta/unstable-book/language-features/try-blocks.html, https://github.com/rust-lang/rust/issues/31436)
Usage example:
#![feature(try_blocks)]
fn main() {
// you need to define the result type explicitly
let result: Result<(), Error> = try {
do_step_1()?;
do_step_2()?;
do_step_3()?;
};
if let Err(e) = result {
println!("Failed to perform necessary steps, ({:?})", e);
}
}
fn do_step_1() -> Result<(), Error> { Ok(()) }
fn do_step_2() -> Result<(), Error> { Ok(()) }
fn do_step_3() -> Result<(), Error> { Err(Error::SomeError) }
#[derive(Debug)]
enum Error {
SomeError,
}

I think match expression is equivalent of try/catch
match get_weather(location) {
Ok(report) => {
display_weather(location, &report);
}
Err(err) => {
println!("error querying the weather: {}", err);
// or write a better logic
}
}
We try to get weather report from api, if our request fails, handle the error other wise shows the result.

Not sure it's considered idiomatic Rust, but you can use an anonymous closure to achieve syntax similar to try/catch:
fn do_step_1() -> Result<(), String> { Ok(()) }
fn do_step_2() -> Result<(), String> { Err("error at step 2".to_string()) }
fn do_step_3() -> Result<(), String> { Ok(()) }
fn alert_user(s: &str) { println!("{}", s); }
fn main() {
(|| {
do_step_1()?;
do_step_2()?;
do_step_3()?;
Ok(())
})().unwrap_or_else(|_err: String| {
alert_user("Failed to perform the necessary steps");
})
}

The try and except concept is used in extremely vague terms. Since Rust is a strongly typed language, the user must write their own methods for handling errors by relying on the provided Option<T> and Result<T, E> enumerations or by defining their own accustomed enumerations.
See here for a more in-depth read for error-handling using enumerations.
The try macro is deprecated and has been replaced with the ? operator which makes it easier to organize and clean up error-handling because it can get messy. A main use for the ? operator would be that it allows you to implement the From trait for Result<T, E>'s Err(E) variant.
Here's a basic example:
use std::num::ParseIntError;
// Custom error-based enum with a single example
#[derive(Debug)]
enum Error {
ParseIntError(ParseIntError),
// Other errors...
}
// Then implement the `From` trait for each error so that the `?` operator knows what to do for each specified error.
impl From<ParseIntError> for Error {
fn from(error: ParseIntError) -> Self {
Self::ParseIntError(error)
}
}
// When using the `?` try operator, if the `Result` is an `Err` then it will basically act as `return Err(E)` returning that error value out to the current scope. If it is `Ok(T)`, it will simply unwrap the variant.
fn main() -> Result<(), Error> {
// This will return the value `69` as a `u8` type
let parsed_value_1 = "69".parse::<u8>()?;
println!("{}", parsed_value_1);
// Since parsing fails here, a `ParseIntError` will be returned to the current function. *Since the scope is the `main` function, it will automatically print the error after panicking.
let parsed_value_2 = "poop".parse::<u8>()?;
// Unreachable code
println!("{}", parsed_value_2);
Ok(())
}

Related

How can I easily get a reference to a value after it has been moved into a tuple-type enum variant?

I want to move a value into a tuple-type enum variant and obtain a reference to the value after it has been moved. I see how this is possible with an if let statement, but this seems like this should be unnecessary when the particular variant is known statically.
Is there any way to get the reference to the moved value without requiring an if let or match?
This code block is a simple illustration of my question (see below for a more challenging case):
enum Transport {
Car(u32), // horsepower
Horse(String), // name
}
fn do_something(x: &String) {
println!(x);
}
fn main() {
// Can I avoid needing this if, which is clearly redundant?
if let Transport::Horse(ref name) = Transport::Horse("daisy".into()) {
do_something(name);
}
else {
// Can never happen
}
// I tried the following, it gives:
// "error[E0005]: refutable pattern in local binding: `Car(_)` not covered"
let Transport::Horse(ref name) = Transport::Horse("daisy".into());
}
It is easy to find ways to side-step the issue in the above code, since there are no real interface requirements. Consider instead the following example, where I am building a simple API for building trees (where each node can have n children). Nodes have an add_child_node method returning a reference to the node that was added, to allow chaining of calls to quickly build deep trees. (It is debatable whether this is a good API, but that is irrelevant to the question). add_child_node must return a mutable reference to the contents of an enum variant. Is the if let required in this example (without changing the API)?
struct Node {
children: Vec<Child>,
// ...
}
enum Child {
Node(Node),
Leaf
}
impl Node {
fn add_child_node(&mut self, node: Node) -> &mut Node {
self.children.push(Child::Node(node));
// It seems like this if should be unnecessary
if let Some(&mut Child::Node(ref mut x)) = self.children.last() {
return x;
}
// Required to compile, since we must return something
unreachable!();
}
fn add_child_leaf(&mut self) {
// ...
}
}
No. You can use unreachable!() for the else case, and it's usually clear even without message/comment what's going on. The compiler is also very likely to optimize the check away.
If the variants have the same type you can implement AsRef and use the Transport as a &str:
enum Transport {
Car(String),
Horse(String),
}
fn do_something<S: AsRef<str>>(x: &S) {
println!("{}", x.as_ref());
}
impl AsRef<str> for Transport {
fn as_ref(&self) -> &str {
match self {
Transport::Car(s) => s,
Transport::Horse(s) => s,
}
}
}
fn main() {
let transport = Transport::Horse("daisy".into());
do_something(&transport)
}
Playground
Otherwise you need to use a let if binding as you are doing. No need to use an else clause if you don't want to:
if let Transport::Horse(ref name) = Transport::Horse("daisy".into()) {
do_something(name);
}
define From<Transport> for String:
…
impl From<Transport> for String {
fn from(t: Transport) -> String {
match t {
Transport::Car(value) => value.to_string(),
Transport::Horse(name) => name,
}
}
}
fn do_something(x: Transport) {
println!("{}", String::from(x));
}
fn main() {
let horse = Transport::Horse("daisy".to_string());
let car = Transport::Car(150);
do_something(horse);
do_something(car);
}

rust extend built-in enum Result?

tl;dr Is it possible to extend std::result::Result to add my own variant that signals "things are Okay but also..." and keep impl Result methods like is_ok()?
I want to extend Result to signal additional states that a function caller can use for special cases.
use std::result::Result
use std::io::Error;
/// Extend Result to also signal "things are okay but check on things"
enum ResultExt<T, E> {
Result<T, E>,
OkButCheckThings(T),
}
pub fn do_stuff() -> ResultExt<u64, Error> {
// ...
}
pub fn main() -> {
let var = match do_stuff() {
Ok(val) => { val },
Err(err) => { 0 },
OkButCheckThings(val) => { check_things(); val },
}
dbg!(var);
}
It's possible to plainly extend an Enum. But I would also like to use the underlying Result<T, E> functions like is_ok.
let var2 = do_stuff();
if var2.is_ok() {
println!("It is totally Ok, nothing to check!");
}
I created a rust playground example that successfully extends Result<T, E> but the extended enum cannot use functions like is_ok().
The real-world use-case is a function that calls std::io::Read may need to "modify" the returned Result to signal additional states beyond Ok and Err. But I want these various "meta states" to be captured by one enum, as opposed to returning various other bool flags (I want to avoid return signature with (Result<T>, bool, bool). This would allow one clean match statement of all possible states; Ok, Err, "Okay but...", "Err but ...", etc..
There is no current way of "extending" and enum perse.
But it could be simply solved by embedding your own enum type into the result itself.
Simple example, similar to yours:
use std::fmt::Display;
enum StuffToCheck<T> {
Ok(T),
CheckThis(T),
}
impl<T> StuffToCheck<T>
where
T: Display + Copy,
{
pub fn check_things(&self) -> T {
match self {
Self::Ok(val) => {
*val
}
Self::CheckThis(val) => {
println!("Checking stuff for {}", val);
*val
}
}
}
}
fn do_stuff() -> ResultExt<u64> {
Ok(StuffToCheck::CheckThis(10))
}
type ResultExt<T> = Result<StuffToCheck<T>, std::io::Error>;
fn main() {
let var = match do_stuff() {
Ok(result) => result.check_things(),
Err(_err) => 0,
};
dbg!(var);
}
Playground
You could even use nested pattern matching:
...
match do_stuff() {
Err(e) => {//handle error}
Ok(StuffToCheck::Ok(value)) => { value },
Ok(StuffToCheck::CheckThis(value)) => {
check_things(value);
value
}
}
...
I think this is an instance of the X-Y problem. You can use the built-in result, you just need a different error type, that returns an option: Some(partial_result) or None.
For example you have function parse, that can attempt to adjust for a malformed input, but report the error.
pub fn parse(b: &str) -> Result<&str, CustomParseError> {
// Do something that might fail,
if failed(){
return CustomParseError::new(None)
} else if partially_failed() {
return CustomParseError::new(Some(partial_result))
} else {
return completeResult
}
}
This way you have a clean code path where nothing failed, and all of your assumptions are correct, and if it's not => instead of unwrapping, you match and check which case you have. This is vastly superior, because the error often contains enough information for you to reconstruct both what went wrong, and what could be done to fix it.

How do I idiomatically unwrap Rust errors with functions rather than closures?

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?
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)
}
Playground

Can I automatically return Ok(()) or None from a function?

I have functions that return an Option or a Result:
fn get_my_result() -> Result<(), Box<Error>> {
lots_of_things()?;
Ok(()) // Could this be omitted?
}
fn get_my_option() -> Option<&'static str> {
if some_condition {
return Some("x");
}
if another_condition {
return Some("y");
}
None // Could this be omitted as well?
}
Currently, neither Ok(()) or None are allowed to be omitted, as shown in the examples above. Is there a reason for that? Is it possible for this to be changed in the future?
Update
We can use Fehler to write code like this:
#[throws(Box<Error>)]
fn get_my_result() {
let value = lots_of_things()?;
// No need to return Ok(())
}
Fehler also allows to throw as Option.
You cannot omit this in Rust. A proposal was made to allow a () → Result<(), _> coercion rule, but it was massively downvoted and then refused.
A comment explains well why it is a bad idea:
I've gotten very wary of implicit coercion because of JavaScript (yes,
I know that's an extreme). I have always loved the explicitness of
Rust, and that's why I have favored the other RFC more.
Here is an example of something I'm afraid of
let x = {
// Do some stuff
...
if blah {
Ok(())
} else {
Err("oh no");
}
};
if let Ok(_) = x {
println!("this always prints");
}
Oops... In this case, the type system actually would give false
confidence. Scary.
Also, more generally I would like the solution to be specific to
exiting a function or block.
When I have a lot of Ok(()) in my code, I create a small helper function to make the code prettier:
fn ok<E>() -> Result<(), E> {
Ok(())
}

Is there a compact and idiomatic way to print an error and return without returning the error?

I'm writing a function that will be called in an infinite loop and only execute something when getting well-formed data from a web-service. If the service is down, returns non-json, or returns json we do not understand, the function should just log the error and return (to be called again after a pause).
I found myself copying and pasting something like this:
let v = match v {
Ok(data) => data,
Err(error) => {
println!("Error decoding json: {:?}", error);
return;
}
};
The body of the error matcher would be different each time. Sometimes it's panic, sometimes it has different messages, and sometimes elements of error could be broken down further to form a better message, but the rest of the construct would be the same.
Is there a shorthand for this? I'm aware of the ? syntax, but that's for propagation. I don't feel that propagation will help with the scenario when you need slightly different processing in case of the error like in the scenario described above. This is because the particular differences in handling belong right here, not up the stack.
I have not written a lot of code in Rust yet so it is very likely that I'm missing something obvious.
In C#, the above would look something like this:
if (v == null)
{
Console.WriteLine("Error decoding json!");
return;
}
or
if (error != null)
{
Console.WriteLine($"Error decoding json: {error}");
return;
}
both of which is much less verbose than in Rust.
If I understood the comments below, one way of shortening would be something like this:
if let Err(error) = v {
println!("Error decoding json: {:?}", error);
return;
}
let v = v.unwrap();
This looks more compact, thank you. Is this idiomatic? Would you write it this way?
I don't feel that propagation will help with the scenario when you need slightly different processing in case of the error like in the scenario described above. This is because the particular differences in handling belong right here, not up the stack.
This is something a custom error type can help with. In this case you have a common behavior ("log an error") and you want to do that in slightly different ways for different values. It makes sense to move the "log an error" part up to the caller (let's call the function try_poll):
loop {
if let Err(e) = try_poll() {
println!("{}", e);
}
sleep(100);
}
And create a type that implements Display, and From<E> for each error type E:
enum PollError {
NetworkError(NetworkError),
JsonParseError(JsonParseError),
}
impl fmt::Display for PollError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
PollError::NetworkError(ref e) => write!(f, "Error downloading file: {:?}", e),
PollError::JsonParseError(ref e) => write!(f, "Error parsing JSON: {:?}", e),
}
}
}
impl From<NetworkError> for PollError {
fn from(e: NetworkError) -> Self {
PollError::NetworkError(e)
}
}
impl From<JsonParseError> for PollError {
fn from(e: JsonParseError) -> Self {
PollError::JsonParseError(e)
}
}
Now you can use ? to propagate the error, but the caller still doesn't have to be concerned with which error specifically it is.
fn try_poll() -> Result<(), PollError> {
let data = try_fetch_content()?;
let json = try_parse_json(data)?;
println!("Parsed {:?}", json);
Ok(())
}
(playground)
Ok, I want that, but without all the From implementations.
The tedious part about this is all the impl Froms, which are necessary because of the custom error type. If the only thing that will ever be done with an error is log and ignore it, a custom error type is not particularly useful -- the only thing that really needs to be returned is the error message itself.
In that case, have try_poll instead return Result<(), String>, and use Result::map_err to turn each individual error immediately into an error message, before using ? to propagate it:
fn try_poll() -> Result<(), String> {
let data = try_fetch_content()
.map_err(|e| format!("Error downloading file: {:?}", e))?;
let json = try_parse_json(data)
.map_err(|e| format!("Error parsing JSON: {:?}", e))?;
println!("Parsed {:?}", json);
Ok(())
}
(playground)
The first edition of The Rust Programming Language has this to say about String as an error type:
A rule of thumb is to define your own error type, but a String error type will do in a pinch, particularly if you're writing an application. If you're writing a library, defining your own error type should be strongly preferred so that you don't remove choices from the caller unnecessarily.
As an alternative to a custom macro_rule you could also use ? with Option<T> and a trait extension for Result to print errors and convert successful values.
Playground
pub trait ResultOkPrintErrExt<T> {
fn ok_or_print_err(self, msg: &str) -> Option<T>;
}
impl<T, E> ResultOkPrintErrExt<T> for Result<T, E>
where
E: ::std::fmt::Debug,
{
fn ok_or_print_err(self, msg: &str) -> Option<T> {
match self {
Ok(v) => Some(v),
Err(e) => {
eprintln!("{}: {:?}", msg, e);
None
}
}
}
}
fn read_input() -> Result<u32, ()> {
// Ok(5)
Err(())
}
fn run() -> Option<()> {
let v: u32 = read_input().ok_or_print_err("invalid input")?;
println!("got input: {}", v);
Some(())
}
fn main() {
run();
}

Resources