1.0 - Introduction
Rust error handling is based on two things: enumerations and matching. Rust's enumerations are more
powerful than those of C++ and C#. Each element of the enumeration may wrap some type, as shown in the
example below.
Error handling in Rust does not use exceptions. Instead, it uses error returns, but those are much more
useful than simply returning error codes. Because Result<T, E> is a Rust style enumeration,
functions can return either a result of computation or an error with a single return value. The
functions for testing those conditions are all part of the enum.
Result Type
enum Result<T, E> { Ok(T), Err(E), }
References:std::result std::error::Error
The example below shows how Result<T, E> is used.
Functions returning Result or Option
use display::{*};
fn demo_result<'a>(p: bool) -> Result<&'a str, &'a str> {
print!("\n value of input predicate is {}", p);
if p {
return Ok("it's ok");
}
else {
return Err("not ok");
}
}
fn demo_option<'a>(p:bool) -> Option<&'a str> {
print!("\n value of input predicate is {}", p);
if p {
return Some("something just for you!");
}
else {
return None;
}
}
Output:
-- demo Result --
-----------------------
-- using match
value of input predicate is true
result is it's ok
value of input predicate is false
result is not ok
-- using expect
value of input predicate is true
result is it's ok
-- demo Option --
-----------------------
--using match
value of input predicate is true
something just for you!
value of input predicate is false
sorry, nothing here
--using unwrap
value of input predicate is true
something just for you!
That's all folks!
Using code:
use display::{*};
sub_title(" -- demo Result -- ");
shows("\n-- using match");
let r = demo_result(true);
match r {
Ok(rslt) => print!("\n result is {}", rslt),
Err(rslt) => print!("\n result is {}", rslt)
}
let r = demo_result(false);
match r {
Ok(rslt) => print!("\n result is {}", rslt),
Err(rslt) => print!("\n result is {}", rslt)
}
shows("\n\n-- using expect");
let r = demo_result(true)
.expect("predicate was false");
print!("\n result is {}", r);
/////////////////////////////////////////////
// uncomment to see panic
// let _r = demo_result(false)
// .expect("predicate was false");
putline();
sub_title(" -- demo Option -- ");
shows("\n--using match");
let r = demo_option(true);
match r {
Some(rslt) => print!("\n {}", rslt),
None => print!("\n sorry, nothing here")
}
let r = demo_option(false);
match r {
Some(rslt) => print!("\n {}", rslt),
None => print!("\n sorry, nothing here")
}
shows("\n\n--using unwrap");
let r = demo_option(true).unwrap();
print!("\n {}", r);
/////////////////////////////////////////////
// uncomment to see panic
// let _r = demo_option(false).unwrap();
print!("\n\n That's all folks!\n\n");
2.0 - Bubbling Up Errors
Rust requires Result<T, E>, returned from functions, to be handled, or explicitly
ignored by binding the result to a variable with leading underscore, e.g.
let _ign = function_returning_result(...);
Otherwise, it must match against the returned result, as shown above. If a function calls many other
functions that return results, the code gets cluttered with all the error handling. One very nice way
to avoid that is to use error bubbling.
If we suffix the function call with a ?, called try operator, then if the result is not an error, ? binds
the unwrapped T value from Result<T, E>. So the expression:
let ret_val = function_returns_result()?
Binds the result value to ret_val. If, on the other hand, the function returns an error, then
operator ? immediately returns Err(Error) to the caller.
This is illustrated in the example below:
Error Bubbling
#[derive(Debug)]
struct Error;
#[derive(Debug)]
struct Demo;
impl Demo {
fn do_int(&self, i:i32) -> &Self {
print!("\n my argument is {}", i);
&self
}
fn do_float(&self, f:f64) -> &Self {
print!("\n my argument is {}", f);
&self
}
fn do_vec(&self, v:Vec<i32>)
-> &Self {
print!("\n my argument is {:?}", v);
&self
}
fn do_err(&self, p:bool)
-> Result<String, Error> {
let e = Error {};
if p {
Ok("no error".to_string())
} else {
Err(e)
}
}
}
fn main() -> Result<(),Error> {
let d = Demo {};
let rslt = d.do_int(42)
.do_float(3.14159)
.do_vec(vec![1,2,3])
.do_err(true);
print!("\n rslt = {:?}", rslt);
/*-------------------------------------------
Bubbling up Errors
If do_err returns Ok(value) then bind
value to rslt,
else return Err(Error).
*/
let rslt = d.do_int(42)
.do_float(3.1415927)
.do_err(true)?; // binds to rslt
print!("\n rslt = {:?}", rslt);
let rslt = d.do_int(42)
.do_float(3.1415927)
.do_err(false)?; // returns Err
print!("\n rslt = {:?}", rslt);
Ok(())
}
Output
Compiling playground v0.0.1 (/playground)
Finished dev [unoptimized + debuginfo]
target(s) in 3.83s
Running `target/debug/playground`
Error: Error
Standard Output
my argument is 42
my argument is 3.14159
my argument is [1, 2, 3]
rslt = Ok("no error")
my argument is 42
my argument is 3.1415927
rslt = "no error"
my argument is 42
my argument is 3.1415927
References:
playground example
Error handling - The Rust Book