wiki/content/20200916163737-recoverable_errors.md

172 lines
3.8 KiB
Markdown
Raw Permalink Normal View History

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 youre 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)
}
}
```