mirror of
https://github.com/google/comprehensive-rust.git
synced 2025-05-14 14:46:43 +02:00
error-handling: split thiserror into its own slide (#2332)
`thiserror` is best understood as a way to eliminate boilerplate on the patterns we've already seen, and then we can show it in conjunction with `anyhow` subsequently. Fixes #2027.
This commit is contained in:
parent
db0b161424
commit
aeb643f380
@ -138,8 +138,9 @@ use-boolean-and = true
|
|||||||
"error-handling/converting-error-types-example.html" = "../error-handling/try-conversions.html"
|
"error-handling/converting-error-types-example.html" = "../error-handling/try-conversions.html"
|
||||||
"error-handling/converting-error-types.html" = "../error-handling/try-conversions.html"
|
"error-handling/converting-error-types.html" = "../error-handling/try-conversions.html"
|
||||||
"error-handling/deriving-error-enums.html" = "../error-handling/error.html"
|
"error-handling/deriving-error-enums.html" = "../error-handling/error.html"
|
||||||
"error-handling/dynamic-errors.html" = "../error-handling/thiserror-and-anyhow.html"
|
"error-handling/dynamic-errors.html" = "../error-handling/anyhow.html"
|
||||||
"error-handling/error-contexts.html" = "../error-handling/thiserror-and-anyhow.html"
|
"error-handling/error-contexts.html" = "../error-handling/anyhow.html"
|
||||||
|
"error-handling/thiserror-and-anyhow.html" = "../error-handling/anyhow.html"
|
||||||
"error-handling/panic-unwind.html" = "../error-handling/panics.html"
|
"error-handling/panic-unwind.html" = "../error-handling/panics.html"
|
||||||
"error-handling/try-operator.html" = "../error-handling/try.html"
|
"error-handling/try-operator.html" = "../error-handling/try.html"
|
||||||
"exercises/concurrency/afternoon.html" = "../../concurrency/async-exercises.html"
|
"exercises/concurrency/afternoon.html" = "../../concurrency/async-exercises.html"
|
||||||
|
@ -197,7 +197,8 @@
|
|||||||
- [Try Operator](error-handling/try.md)
|
- [Try Operator](error-handling/try.md)
|
||||||
- [Try Conversions](error-handling/try-conversions.md)
|
- [Try Conversions](error-handling/try-conversions.md)
|
||||||
- [`Error` Trait](error-handling/error.md)
|
- [`Error` Trait](error-handling/error.md)
|
||||||
- [`thiserror` and `anyhow`](error-handling/thiserror-and-anyhow.md)
|
- [`thiserror`](error-handling/thiserror.md)
|
||||||
|
- [`anyhow`](error-handling/anyhow.md)
|
||||||
- [Exercise: Rewriting with `Result`](error-handling/exercise.md)
|
- [Exercise: Rewriting with `Result`](error-handling/exercise.md)
|
||||||
- [Solution](error-handling/solution.md)
|
- [Solution](error-handling/solution.md)
|
||||||
- [Unsafe Rust](unsafe-rust.md)
|
- [Unsafe Rust](unsafe-rust.md)
|
||||||
|
@ -2,16 +2,14 @@
|
|||||||
minutes: 5
|
minutes: 5
|
||||||
---
|
---
|
||||||
|
|
||||||
# `thiserror` and `anyhow`
|
# `anyhow`
|
||||||
|
|
||||||
The [`thiserror`](https://docs.rs/thiserror/) and
|
The [`anyhow`](https://docs.rs/anyhow/) crate provides a rich error type with
|
||||||
[`anyhow`](https://docs.rs/anyhow/) crates are widely used to simplify error
|
support for carrying additional contextual information, which can be used to
|
||||||
handling.
|
provide a semantic trace of what the program was doing leading up to the error.
|
||||||
|
|
||||||
- `thiserror` is often used in libraries to create custom error types that
|
This can be combined with the convenience macros from `thiserror` to avoid
|
||||||
implement `From<T>`.
|
writing out trait impls explicitly for custom error types.
|
||||||
- `anyhow` is often used by applications to help with error handling in
|
|
||||||
functions, including adding contextual information to your errors.
|
|
||||||
|
|
||||||
```rust,editable,compile_fail
|
```rust,editable,compile_fail
|
||||||
use anyhow::{bail, Context, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
@ -46,25 +44,23 @@ fn main() {
|
|||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
|
||||||
## `thiserror`
|
|
||||||
|
|
||||||
- The `Error` derive macro is provided by `thiserror`, and has lots of useful
|
|
||||||
attributes to help define error types in a compact way.
|
|
||||||
- The `std::error::Error` trait is derived automatically.
|
|
||||||
- The message from `#[error]` is used to derive the `Display` trait.
|
|
||||||
|
|
||||||
## `anyhow`
|
|
||||||
|
|
||||||
- `anyhow::Error` is essentially a wrapper around `Box<dyn Error>`. As such it's
|
- `anyhow::Error` is essentially a wrapper around `Box<dyn Error>`. As such it's
|
||||||
again generally not a good choice for the public API of a library, but is
|
again generally not a good choice for the public API of a library, but is
|
||||||
widely used in applications.
|
widely used in applications.
|
||||||
- `anyhow::Result<V>` is a type alias for `Result<V, anyhow::Error>`.
|
- `anyhow::Result<V>` is a type alias for `Result<V, anyhow::Error>`.
|
||||||
- Actual error type inside of it can be extracted for examination if necessary.
|
- Functionality provided by `anyhow::Error` may be familiar to Go developers, as
|
||||||
- Functionality provided by `anyhow::Result<T>` may be familiar to Go
|
it provides similar behavior to the Go `error` type and
|
||||||
developers, as it provides similar usage patterns and ergonomics to
|
`Result<T, anyhow::Error>` is much like a Go `(T, error)` (with the convention
|
||||||
`(T, error)` from Go.
|
that only one element of the pair is meaningful).
|
||||||
- `anyhow::Context` is a trait implemented for the standard `Result` and
|
- `anyhow::Context` is a trait implemented for the standard `Result` and
|
||||||
`Option` types. `use anyhow::Context` is necessary to enable `.context()` and
|
`Option` types. `use anyhow::Context` is necessary to enable `.context()` and
|
||||||
`.with_context()` on those types.
|
`.with_context()` on those types.
|
||||||
|
|
||||||
|
# More to Explore
|
||||||
|
|
||||||
|
- `anyhow::Error` has support for downcasting, much like `std::any::Any`; the
|
||||||
|
specific error type stored inside can be extracted for examination if desired
|
||||||
|
with
|
||||||
|
[`Error::downcast`](https://docs.rs/anyhow/latest/anyhow/struct.Error.html#method.downcast).
|
||||||
|
|
||||||
</details>
|
</details>
|
51
src/error-handling/thiserror.md
Normal file
51
src/error-handling/thiserror.md
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
---
|
||||||
|
minutes: 5
|
||||||
|
---
|
||||||
|
|
||||||
|
# `thiserror`
|
||||||
|
|
||||||
|
The [`thiserror`](https://docs.rs/thiserror/) crate provides macros to help
|
||||||
|
avoid boilerplate when defining error types. It provides derive macros that
|
||||||
|
assist in implementing `From<T>`, `Display`, and the `Error` trait.
|
||||||
|
|
||||||
|
```rust,editable,compile_fail
|
||||||
|
use std::fs;
|
||||||
|
use std::io::Read;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Error)]
|
||||||
|
enum ReadUsernameError {
|
||||||
|
#[error("I/O error: {0}")]
|
||||||
|
IoError(#[from] io::Error),
|
||||||
|
#[error("Found no username in {0}")]
|
||||||
|
EmptyUsername(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_username(path: &str) -> Result<String, ReadUsernameError> {
|
||||||
|
let mut username = String::with_capacity(100);
|
||||||
|
File::open(path)?.read_to_string(&mut username)?;
|
||||||
|
if username.is_empty() {
|
||||||
|
return Err(ReadUsernameError::EmptyUsername(String::from(path)));
|
||||||
|
}
|
||||||
|
Ok(username)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
//fs::write("config.dat", "").unwrap();
|
||||||
|
match read_username("config.dat") {
|
||||||
|
Ok(username) => println!("Username: {username}"),
|
||||||
|
Err(err) => println!("Error: {err:?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
|
||||||
|
- The `Error` derive macro is provided by `thiserror`, and has lots of useful
|
||||||
|
attributes to help define error types in a compact way.
|
||||||
|
- The message from `#[error]` is used to derive the `Display` trait.
|
||||||
|
- Note that the (`thiserror::`)`Error` derive macro, while it has the effect of
|
||||||
|
implementing the (`std::error::`)`Error` trait, is not the same this; traits
|
||||||
|
and macros do not share a namespace.
|
||||||
|
|
||||||
|
</details>
|
@ -43,7 +43,7 @@ impl Error for ReadUsernameError {}
|
|||||||
impl Display for ReadUsernameError {
|
impl Display for ReadUsernameError {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::IoError(e) => write!(f, "IO error: {e}"),
|
Self::IoError(e) => write!(f, "I/O error: {e}"),
|
||||||
Self::EmptyUsername(path) => write!(f, "Found no username in {path}"),
|
Self::EmptyUsername(path) => write!(f, "Found no username in {path}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user