2024-05-06 20:40:05 +00:00
|
|
|
|
---
|
2024-10-30 17:34:11 +00:00
|
|
|
|
date: 2020-09-16
|
2024-05-06 20:40:05 +00:00
|
|
|
|
id: cbdaa6b4-cf72-45fb-ac16-79afd8900478
|
|
|
|
|
title: Rust Recoverable Errors
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
# match
|
|
|
|
|
|
|
|
|
|
In Rust we use the [match](20201006102934-pattern_syntax) expression to
|
|
|
|
|
check for errors in the
|
|
|
|
|
[Result](https://doc.rust-lang.org/std/result/enum.Result.html):
|
|
|
|
|
|
|
|
|
|
``` rust
|
|
|
|
|
use std::fs::File;
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
let f = File::open("hello.txt");
|
|
|
|
|
|
|
|
|
|
let f = match f {
|
|
|
|
|
Ok(file) => file,
|
|
|
|
|
Err(error) => panic!("Problem opening the file: {:?}", error),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Generally speaking you want to perform different actions depending on
|
|
|
|
|
the error type:
|
|
|
|
|
|
|
|
|
|
``` rust
|
|
|
|
|
use std::fs::File;
|
|
|
|
|
use std::io::ErrorKind;
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
let f = File::open("hello.txt");
|
|
|
|
|
|
|
|
|
|
let f = match f {
|
|
|
|
|
Ok(file) => file,
|
|
|
|
|
Err(error) => match error.kind() {
|
|
|
|
|
ErrorKind::NotFound => match File::create("hello.txt") {
|
|
|
|
|
Ok(fc) => fc,
|
|
|
|
|
Err(e) => panic!("Problem creating the file: {:?}", e),
|
|
|
|
|
},
|
|
|
|
|
other_error => panic!("Problem opening the file: {:?}", other_error),
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Ditching match altogether
|
|
|
|
|
|
|
|
|
|
A more elegant way of writing the above:
|
|
|
|
|
|
|
|
|
|
``` rust
|
|
|
|
|
use std::fs::File;
|
|
|
|
|
use std::io::ErrorKind;
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
let f = File::open("hello.txt").unwrap_or_else(|error| {
|
|
|
|
|
if error.kind() == ErrorKind::NotFound {
|
|
|
|
|
File::create("hello.txt").unwrap_or_else(|error| {
|
|
|
|
|
panic!("Problem creating the file: {:?}", error);
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
panic!("Problem opening the file: {:?}", error);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
# Panic on Error shortcuts
|
|
|
|
|
|
|
|
|
|
## unwrap
|
|
|
|
|
|
|
|
|
|
If the `Result` value is `Ok`,
|
|
|
|
|
[unwrap](https://doc.rust-lang.org/std/option/enum.Option.html#method.unwrap)
|
|
|
|
|
will return the value inside the `Ok`. If the `Result` is of the `Err`
|
|
|
|
|
variant, unwrap will call the `panic!` macro
|
|
|
|
|
|
|
|
|
|
``` rust
|
|
|
|
|
use std::fs::File;
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
let f = File::open("hello.txt").unwrap();
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## expect
|
|
|
|
|
|
|
|
|
|
[expect](https://doc.rust-lang.org/std/option/enum.Option.html#method.expect)
|
|
|
|
|
is almost the same as unwrap, the only difference being that it allws us
|
|
|
|
|
to choose the panic! error message:
|
|
|
|
|
|
|
|
|
|
``` rust
|
|
|
|
|
use std::fs::File;
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
let f = File::open("hello.txt").expect("Failed to open hello.txt");
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
# Propagating Errors
|
|
|
|
|
|
|
|
|
|
When you’re writing a function whose implementation calls something that
|
|
|
|
|
might fail, instead of handling the error within this function, you can
|
|
|
|
|
return the error to the calling code so that it can decide what to do.
|
|
|
|
|
This is known as propagating the error and gives more control to the
|
|
|
|
|
calling code, where there might be more information or logic that
|
|
|
|
|
dictates how the error should be handled than what you have available in
|
|
|
|
|
the context of your code.
|
|
|
|
|
|
|
|
|
|
``` rust
|
|
|
|
|
#![allow(unused)]
|
|
|
|
|
fn main() {
|
|
|
|
|
use std::fs::File;
|
|
|
|
|
use std::io;
|
|
|
|
|
use std::io::Read;
|
|
|
|
|
|
|
|
|
|
fn read_username_from_file() -> Result<String, io::Error> {
|
|
|
|
|
let f = File::open("hello.txt");
|
|
|
|
|
|
|
|
|
|
let mut f = match f {
|
|
|
|
|
Ok(file) => file,
|
|
|
|
|
Err(e) => return Err(e),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let mut s = String::new();
|
|
|
|
|
|
|
|
|
|
match f.read_to_string(&mut s) {
|
|
|
|
|
Ok(_) => Ok(s),
|
|
|
|
|
Err(e) => Err(e),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
This can of course be refactored to something nicer:
|
|
|
|
|
|
|
|
|
|
``` rust
|
|
|
|
|
#![allow(unused)]
|
|
|
|
|
fn main() {
|
|
|
|
|
use std::fs::File;
|
|
|
|
|
use std::io;
|
|
|
|
|
use std::io::Read;
|
|
|
|
|
|
|
|
|
|
fn read_username_from_file() -> Result<String, io::Error> {
|
|
|
|
|
let mut f = File::open("hello.txt")?;
|
|
|
|
|
let mut s = String::new();
|
|
|
|
|
f.read_to_string(&mut s)?;
|
|
|
|
|
Ok(s)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
And this can be refactored even more:
|
|
|
|
|
|
|
|
|
|
``` rust
|
|
|
|
|
#![allow(unused)]
|
|
|
|
|
fn main() {
|
|
|
|
|
use std::fs::File;
|
|
|
|
|
use std::io;
|
|
|
|
|
use std::io::Read;
|
|
|
|
|
|
|
|
|
|
fn read_username_from_file() -> Result<String, io::Error> {
|
|
|
|
|
let mut s = String::new();
|
|
|
|
|
|
|
|
|
|
File::open("hello.txt")?.read_to_string(&mut s)?;
|
|
|
|
|
|
|
|
|
|
Ok(s)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|