Rust
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Ownership and Borrowing
-
Section 4: Structuring Data
-
Section 5: Collections and Error Handling
-
Section 6: Traits and Generics
-
Section 7: Concurrency
-
Section 8: Building for the Web
-
Section 9: Memory and Performance
-
Section 10: More Standard Library and Ecosystem
-
Section 11: Advanced Rust
-
Section 12: Rust for Systems and WebAssembly
-
Section 13: Tooling and Best Practices
-
Section 14: Data Structures and Algorithms in Rust
-
Section 15: Practical Projects
-
Section 16: Interview Practice
-
Section 17: std::collections In Depth
-
Section 18: std::io and std::fs In Depth
-
Section 19: std::net
-
Section 20: std::option and std::result In Depth
-
Section 21: std::iter In Depth
-
Section 22: std::sync In Depth
-
Section 23: std::string and std::str
-
Section 24: Cargo and Crates.io Ecosystem
-
Section 25: Popular Crates Ecosystem
-
Section 26: Rust Memory Model Deep Dive
-
Section 27: More Practice Exercises
-
Section 28: More Interview Practice
-
Section 29: Async Rust Deep Dive
-
Section 30: Tokio Ecosystem In Depth
-
Section 31: Error Handling Ecosystem Deep Dive
-
Section 32: Serde In Depth
-
Section 33: Web Development with Rust Deep Dive
-
Section 34: Database Access Ecosystem
-
Section 35: Rust for Embedded Systems Deep Dive
-
Section 36: Rust Macros In Depth
-
Section 37: Advanced Trait System
-
Section 38: Unsafe Rust In Depth
-
Section 39: Rust CLI Development
-
Section 40: Testing Ecosystem Deep Dive
-
Section 41: WebAssembly Deep Dive
-
Section 42: Rust Design Patterns
-
Section 43: More Data Structures in Rust
-
Section 44: Final Practice Projects
-
Section 45: Rust Performance Optimization
-
Section 46: Rust Ecosystem Tooling
-
Section 47: More Interview and Review
159: Designing Error Types with thiserror
Imagine you're managing a high-end restaurant kitchen. If a cook yells, "Something went wrong!" that's useless. You don't know if the oven caught fire, the fish is spoiled, or someone just dropped a plate. You need specific labels. You need to know: "The oven is at the wrong temperature" (and exactly what that temperature is) or "We are out of sea bass."
In Rust, using a generic Box or a string is like that vague shout of "Something went wrong!" It tells the caller that an error happened, but it doesn't give them a way to react programmatically. thiserror is essentially a way to create a professional, categorized "labeling system" for your errors without spending hours writing the same tedious boilerplate code for every single error type.
Ditching the Display Boilerplate
If you've built a custom error enum before, you know the drill. You define the enum, then you implement std::fmt::Display to make it printable, and then you implement std::error::Error. It's a lot of typing for very little reward. I've spent more time than I'd like to admit writing write!(f, "Configuration file not found") over and over.
With thiserror, we move that logic directly onto the enum variants using the #[error("...")] attribute. Let's look at a real scenario: a small utility that reads a user's configuration file and parses a specific API key.
use thiserror::Error;
use std::io;
use std::num::ParseIntError;
#[derive(Error, Debug)]
pub enum ConfigError {
#[error("could not read configuration file: {0}")]
Io(#[from] io::Error),
#[error("api key must be a number, but found: {0}")]
InvalidKeyFormat(#[from] ParseIntError),
#[error("missing required configuration key: {0}")]
MissingKey(String),
#[error("the configuration file is empty")]
EmptyFile,
}
Handling the "Wrapped" Errors
Notice those #[from] attributes? That's where the real magic happens. In the example above, ConfigError::Io doesn't just label the error; it wraps a standard std::io::Error.
Because of #[from], thiserror automatically implements From<io::Error> for ConfigError. This means if you're inside a function that returns Result<T, ConfigError>, you can use the ? operator on a function that returns an io::Error, and Rust will automatically convert it into your ConfigError::Io variant. It keeps your business logic clean while preserving the original error's context.
When to choose this over anyhow
You've likely seen anyhow in other projects. I get asked a lot: "Why bother with thiserror if anyhow is easier?"
Here is the rule of thumb I follow: Use anyhow for applications (the final binary), and use thiserror for libraries. If you're writing a crate that other people will use, you cannot just return an anyhow::Error. Why? Because the person using your library might want to programmatically check if the error was a MissingKey so they can prompt the user to enter it. If you wrap everything in a generic anyhow blob, you've robbed your user of the ability to handle specific errors gracefully.
By using thiserror, you provide a strongly-typed contract. You're telling the user: "Here are the five specific things that can go wrong in my code, and here is exactly how to identify them."
📋 Practical Task
Building a Secure Vault Error Handler
You are building a "Secure Vault" module that manages encrypted secrets. You need to create a custom error type called VaultError using the thiserror crate that handles the following scenarios:
- Disk Failure: Should wrap a
std::io::Errorusing#[from]. - Decryption Failure: Should wrap a
std::num::ParseIntError(simulating a failed key parse) using#[from]. - Permission Denied: A custom error that takes a
Stringrepresenting the username who was denied access. The error message should be:"User {0} does not have access to this vault". - Vault Locked: A simple unit variant with the message:
"The vault is currently locked by another process".
Requirements:
- Implement the
VaultErrorenum with the appropriatethiserrorderives and attributes. - Write a function
open_vault(user: &str) -> Result<String, VaultError>that returns aVaultError::PermissionDeniedif the username is not "admin", and aVaultError::VaultLockedif a simulated lock is present. - Demonstrate that a
std::io::Errorcan be converted into aVaultErrorusing the?operator inside a function.
There are no comments for now.