You've already forked comprehensive-rust
							
							
				mirror of
				https://github.com/google/comprehensive-rust.git
				synced 2025-10-31 08:37:45 +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:
		
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			
						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.html" = "../error-handling/try-conversions.html" | ||||
| "error-handling/deriving-error-enums.html" = "../error-handling/error.html" | ||||
| "error-handling/dynamic-errors.html" = "../error-handling/thiserror-and-anyhow.html" | ||||
| "error-handling/error-contexts.html" = "../error-handling/thiserror-and-anyhow.html" | ||||
| "error-handling/dynamic-errors.html" = "../error-handling/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/try-operator.html" = "../error-handling/try.html" | ||||
| "exercises/concurrency/afternoon.html" = "../../concurrency/async-exercises.html" | ||||
|   | ||||
| @@ -197,7 +197,8 @@ | ||||
|   - [Try Operator](error-handling/try.md) | ||||
|   - [Try Conversions](error-handling/try-conversions.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) | ||||
|     - [Solution](error-handling/solution.md) | ||||
| - [Unsafe Rust](unsafe-rust.md) | ||||
|   | ||||
| @@ -2,16 +2,14 @@ | ||||
| minutes: 5 | ||||
| --- | ||||
| 
 | ||||
| # `thiserror` and `anyhow` | ||||
| # `anyhow` | ||||
| 
 | ||||
| The [`thiserror`](https://docs.rs/thiserror/) and | ||||
| [`anyhow`](https://docs.rs/anyhow/) crates are widely used to simplify error | ||||
| handling. | ||||
| The [`anyhow`](https://docs.rs/anyhow/) crate provides a rich error type with | ||||
| support for carrying additional contextual information, which can be used to | ||||
| 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 | ||||
|   implement `From<T>`. | ||||
| - `anyhow` is often used by applications to help with error handling in | ||||
|   functions, including adding contextual information to your errors. | ||||
| This can be combined with the convenience macros from `thiserror` to avoid | ||||
| writing out trait impls explicitly for custom error types. | ||||
| 
 | ||||
| ```rust,editable,compile_fail | ||||
| use anyhow::{bail, Context, Result}; | ||||
| @@ -46,25 +44,23 @@ fn main() { | ||||
| 
 | ||||
| <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 | ||||
|   again generally not a good choice for the public API of a library, but is | ||||
|   widely used in applications. | ||||
| - `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::Result<T>` may be familiar to Go | ||||
|   developers, as it provides similar usage patterns and ergonomics to | ||||
|   `(T, error)` from Go. | ||||
| - Functionality provided by `anyhow::Error` may be familiar to Go developers, as | ||||
|   it provides similar behavior to the Go `error` type and | ||||
|   `Result<T, anyhow::Error>` is much like a Go `(T, error)` (with the convention | ||||
|   that only one element of the pair is meaningful). | ||||
| - `anyhow::Context` is a trait implemented for the standard `Result` and | ||||
|   `Option` types. `use anyhow::Context` is necessary to enable `.context()` and | ||||
|   `.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> | ||||
							
								
								
									
										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 { | ||||
|     fn fmt(&self, f: &mut Formatter) -> fmt::Result { | ||||
|         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}"), | ||||
|         } | ||||
|     } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user