Hello everyone, new episode of rust series is here, today we will discuss more about Error handling in Rust. If you missed previous episode about error handling I recommend to go back and start there. Today we will find more about Option and Result types and explain very useful try! macro.
Before we go to the macro let's recapitulate that we have two standard return types in Rust which are Option and Result. Let's check basic methods these types provide. This is just a short summary of basic (and mostly very simple) methods for a brief overview but it's good to know what's provided so we can use it. Some of them we've already met .
Option methods
- is_some - returns true if option is Some value
- is_none - returns true if option is None value
- as_ref - returns
Option<&T>
- as_mut - returns
Option<&mut T>
- expect(msg: &str) - returns value of Some , panic with message otherwise
- unwrap - returns value of Some, panics otherwise
- unwrap_or(default : T) - returns value of Some, default otherwise
- unwrap_or_else(f: F) - returns value of Some or closure computed value otherwise
- map(f:F) - maps
Option<T>
toOption<U>
by using closure F - map_or(default: U, f: F) - applies function or returns default
- map_or_else - applies function, or computes default
- ok_or(err: E) - transforms
Option<T>
toResult<T,E>
- ok_or_else(err: F) - transforms
Option<T>
toResult<T,E>
with using err() - iter - returns iterator over possibly contained value
- iter_mut - return mutable iterator over the possibly contained value
and(optb: Option<U>)
- returns None if option is None, otherwise optb- and_then(F) - returns None if option is None, calls f otherwise and return result
- or(optb) - returns option if it contains value, otherwise returns optb
- or_else(F) - returns the option if it contains value, otherwise f and returns result
- take - takes value of the option and leave it None;
- cloned - maps
Option<&T>
to anOption<T>
- unwrap_or_default - returns the contained value or a default value for that type
Result methods
For Result type, there are many similar methods for Result as for Option with the same or very similar behavior: as_ref, as_mut, map, map_err, iter,iter_mut_ and, and_then, or, or_else, unwrap, unwrap_or_else and some specific methods:
- is_ok - returns true if result is Ok
- is_err - returns true if result is Err
- ok - converts
Result<T,E>
toOption<T>
- err - converts
Result<T,E>
toOption<E>
These methods are mainly useful when we are evaluating results, performing logical operations and translating them each other.
let r1 : Result<i32,i32> = Ok(1);
let r2 : Result<i32,i32> = Ok(2);
let r3 = r1.or(r2);
println!("{:?}", r3);
# output
Ok(1)
try! macro
It's time to mention try! macro which is widely used because it's very handy for error handling. While dealing with standard I/O operations flow, we can for example start with calling File::open("non-existing.txt"). This function returns some Result return type value. Now what? We need to destructure it and handle possible error somehow.
Handling without try!
First try it with an approach using a facility we already know:
use std::io;
use std::fs::{File};
fn work_with_file() -> std::io::Result<()> {
let result = File::open("non-existing.txt");
match result {
Err(e) => return Err(e),
Ok(f) => f
};
// maybe some more work with file
// ...
// anyway now everything went fine and we need to return and report it
Ok(())
}
fn main() {
let result = work_with_file();
// error handling
if let Err(e) = result {
println!("{:?}", e);
} else {
println!("Everything went just fine");
}
}
# output
Error { repr: Os { code: 2, message: "No such file or directory" } }
Good, it works fine. But sooner or later you will find that matching for these simple cases is very bothering. All right, now let's try to simplify it with try! macro
Handling with try!
use std::io;
use std::fs::{File};
fn work_with_file() -> std::io::Result<()> {
let result = File::open("newfile4.txt");
try!(result);
// maybe some more work with file
// ...
// anyway now everything went fine and we need to return and report it
Ok(())
}
fn main() {
let result = work_with_file();
// error handling
if let Err(e) = result {
println!("Cannot open file: {:?}", e);
} else {
println!("Everything's fine");
}
}
# output
Error { repr: Os { code: 2, message: "No such file or directory" } }
You see, all the match statement can be replaced with one single macro. Cool, right? We saved three lines of code and get the same thing. Imagine you have several such operations and now you can see the value. So when you need to propagate error to a caller and work with Ok result try! macro is exactly what you need as you can see from its definition.
macro_rules! try {
($e:expr) => (match $e {
Ok(val) => val,
Err(err) => return Err(err),
});
}
std::io::ErrorKind
One more thing. We usually don't want to report error messages like this. There are many ways howto handle it. One of them (speaking with I/O errors) is using ErrorKind. When get Error we get std::io::Error struct. When we want to have more details about the error, we can call kind() to get ErrorKind which gives us specific variant of that error. Then we match and map it to our custom errors or error messages or whatever we need.
pub enum ErrorKind {
NotFound,
PermissionDenied,
ConnectionRefused,
ConnectionReset,
ConnectionAborted,
NotConnected,
AddrInUse,
AddrNotAvailable,
// ... and many others
}
and then we can obtain specific error variant for our further needs.
let errorKind : ErrorKind = result.kind();
println!("{:?}", errorKind);
# output
NotFound
Now, with this information we can easily map the error type to our desired error palette to report it to a user in some "nice" way or to do with it whatever we wish.
We will not go into other things today but there is more to error handling and around this topic in general and so don't worry we will surely return to this important issues in some coming episode. Anyway, now you should be able to perform basic error handling quite fine.
Postfix
That's all for today, thank you for your appreciations, feel free to comment and point out possible mistakes (first 24 hours works the best but any time is fine). May Jesus bless your programming skills, use them wisely and see you next time.
Meanwhile you can also check the official documentation for more details if you wish.
This post has been linked to from another place on Steem.
Learn more about linkback bot v0.4. Upvote if you want the bot to continue posting linkbacks for your posts. Flag if otherwise.
Built by @ontofractal
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit