Die richtige Rust-Fehlerbehandlung wählen: anyhow, thiserror oder snafu?
Grace Collins
Solutions Engineer · Leapcell

Fehlerbehandlung ist ein unverzichtbarer Bestandteil der Rust-Entwicklung. Rusts Result<T, E>
bietet grundlegende Unterstützung, aber die spezifische Implementierung variiert je nach Szenario. Dieser Artikel stellt drei häufig verwendete Tools zur Fehlerbehandlung vor – anyhow
, thiserror
und snafu
– analysiert ihre Eigenschaften und Anwendungsbereiche und hilft Ihnen anhand praktischer Beispiele zu verstehen, wie Sie sie in Projekten einsetzen können. Egal, ob Sie Anwendungen entwickeln oder Bibliotheken schreiben, dieser Artikel kann als wertvolle Referenz dienen.
Dieser Artikel befasst sich eingehend mit den drei wichtigsten Tools für die Rust-Fehlerbehandlung:
anyhow
eignet sich für die schnelle und einheitliche Fehlerbehandlung, ideal für die Anwendungsentwicklung;thiserror
unterstützt angepasste Fehlertypen und eignet sich daher für die Bibliotheksentwicklung;snafu
bietet eine kontextgesteuerte Fehlerverwaltung, die für komplexe Systeme geeignet ist.
Durch den Vergleich ihrer Stärken und Schwächen und die Demonstration anhand von tatsächlichen Codebeispielen zeigen wir Ihnen, wie Sie das richtige Tool basierend auf den Projektanforderungen auswählen. Wir stellen auch Projekteinrichtungsschritte und Beispielcode bereit, um Ihnen zu helfen, Fehler in Rust effektiver zu behandeln.
Rust-Fehlerbehandlung
Fehlerbehandlung: anyhow
, thiserror
, snafu
anyhow
: einheitliche und einfache Fehlerbehandlung, geeignet für die Programmierung auf Anwendungsebenethiserror
: angepasste und umfangreiche Fehlerbehandlung, geeignet für die Programmierung auf Bibliotheksebenesnafu
: feiner abgestufte Fehlerverwaltung
Hinweis: Achten Sie bei der Entwicklung auf die Größe von Result<T, E>
.
anyhow
-Fehler: Fehlerbehandlung auf Anwendungsebene
Konvertierung und einheitliche Fehlerbehandlung mit anyhow::Error
:
- Bietet einen einheitlichen
anyhow::Error
-Typ, der jeden Fehlertyp unterstützt, derstd::error::Error
implementiert - Verwendet den Operator
?
zur automatischen Fehlerweitergabe, wodurch die mehrschichtige, verschachtelte Fehlerbehandlung vereinfacht wird - Unterstützt das Hinzufügen von dynamischem Kontext (über die Methode
context()
), um die Lesbarkeit von Fehlern zu verbessern
fn get_cluster_info() -> Result<ClusterMap, anyhow::Error> { // Error 3: Err3 let config = std::fs::read_to_string("cluster.json")?; // Error 1: Err1 // let config = std::fs::read_to_string("cluster.json").context("...")?; // Error 1: Err1 let map: ClusterMap = serde_json::from_str(&config)?; // Error 2: Err2 Ok(map) } struct Err1 {...} struct Err2 {...} match ret { Ok(v) => v, Err(e) => return Err(e.into()) } Err1 => Err3: impl From<Err1> for Err3 Err2 => Err3: impl From<Err2> for Err3 impl From<Err1> for Err3 { fn from(v: Err1) -> Err3 { ... } }
thiserror
-Fehler: Definieren von Fehlern auf Bibliotheksebene
- Generiert automatisch Fehlertypen, die
std::error::Error
über Makros entsprechen - Unterstützt verschachtelte Fehlerquellen (mit dem Attribut
#[from]
) und strukturierte Fehlerinformationen - Ermöglicht die Anpassung von Fehlermeldungsvorlagen (z. B.
#[error("Ungültiger Header: {expected}")]
)
Referenz: Rust std::error::Error trait documentation
Da das Error
-Trait sowohl Debug
als auch Display
erfordert:
pub trait Error: Debug + Display {
Es kann wie folgt gedruckt werden:
Error -> println!("{}/ {:?}", err)
snafu
-Fehler: Kontextgesteuerte Fehlerverwaltung
- Konvertiert zugrunde liegende Fehler mithilfe des Makros
Snafu
in domänenspezifische Fehler - Unterstützt das Anhängen von strukturiertem Kontext (z. B. Dateipfade, Eingabeparameter) in der Fehlerkette
- Bietet das Makro
ensure!
, um die Bedingungsprüfung und das Auslösen von Fehlern zu vereinfachen
thiserror
vs. snafu
Weitere Informationen finden Sie unter: kube-rs/kube discussion #453
Vergleichs- und Auswahlleitfaden
Dimension | anyhow | thiserror | snafu |
---|---|---|---|
Fehlertyp | Einheitlicher dynamischer Typ | Statischer benutzerdefinierter Typ | Domänengesteuerter Typ |
Kontextunterstützung | Dynamische Zeichenkette | Strukturfelder | Strukturierte Felder + dynamische Vorlagen |
Geeignete Phase | Anwendungsentwicklung (schnelle Iteration) | Bibliotheksentwicklung (stabile Schnittstellen) | Komplexe Systeme (Wartbarkeit) |
Lernkurve | Niedrig (keine Notwendigkeit, Typen vordefinieren) | Mittel (erfordert das Entwerfen von Fehlerstrukturen) | Hoch (erfordert das Verständnis von Kontextmodellen) |
Typische Benutzer | Frontend-Entwickler / Entwickler von Skripting-Tools | Framework-Entwickler | Infrastruktur-Ingenieure |
Praktische Umsetzung
Erstellen und Initialisieren des Projekts rust-ecosystem-learning
basierend auf einer Vorlage
cargo generate --git git@github.com:qiaopengjun5162/rust-template.git cd rust-ecosystem-learning code .
Projektverzeichnisstruktur
rust-ecosystem-learning on main [!] is 📦 0.1.0 via 🦀 1.85.0 via 🅒 base ➜ tree . -L 6 -I 'target|coverage|coverage_report|node_modules' . ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── _typos.toml ├── cliff.toml ├── deny.toml ├── docs └── src ├── error.rs ├── lib.rs └── main.rs 3 directories, 12 files
Abhängigkeiten hinzufügen
cargo add anyhow cargo add thiserror cargo add serde_json
main.rs
-Datei
use anyhow::Context; use rust_ecosystem_learning::MyError; use std::fs; use std::mem::size_of; fn main() -> Result<(), anyhow::Error> { println!("size of anyhow::Error: {}", size_of::<anyhow::Error>()); println!("size of std::io::Error: {}", size_of::<std::io::Error>()); println!( "size of std::num::ParseIntError: {}", size_of::<std::num::ParseIntError>() ); println!( "size of serde_json::Error: {}", size_of::<serde_json::Error>() ); println!("size of string: {}", size_of::<String>()); println!("size of MyError: {}", size_of::<MyError>()); let filename = "non_existent_file.txt"; let _fd = fs::File::open(filename).with_context(|| format!("Cannot find file: {}", filename))?; fail_with_error()?; Ok(()) } fn fail_with_error() -> Result<(), MyError> { Err(MyError::Custom("This is a custom error".to_string())) } #[cfg(test)] mod tests { #[test] fn it_works() { assert_eq!(2 + 2, 4); } }
lib.rs
-Datei
mod error; pub use error::MyError;
error.rs
-Datei
use thiserror::Error; #[derive(Error, Debug)] pub enum MyError { #[error("IO error: {0}")] Io(#[from] std::io::Error), #[error("Parse error: {0}")] Parse(#[from] std::num::ParseIntError), #[error("Serialize JSON error: {0}")] Serialize(#[from] serde_json::Error), // #[error("Error: {a}, {b:?}, {c:?}, {d:?}")] // BigError { // a: String, // b: Vec<String>, // c: [u8; 64], // d: u64, // }, #[error("Error: {0:?}")] BigError(Box<BigError>), #[error("An error occurred: {0}")] Custom(String), } #[derive(Debug)] pub struct BigError { pub a: String, pub b: Vec<String>, pub c: [u8; 64], pub d: u64, }
Cargo.toml
-Datei
[package] name = "rust-ecosystem-learning" version = "0.1.0" edition = "2021" license = "MIT" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] anyhow = "1.0.97" serde = { version = "1.0.217", features = ["derive"] } serde_json = "1.0.140" thiserror = "2.0.11"
Ausführen des Projekts
rust-ecosystem-learning on main [!] is 📦 0.1.0 via 🦀 1.85.0 via 🅒 base took 2.9s ➜ cargo run Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.02s Running `target/debug/rust-ecosystem-learning` size of anyhow::Error: 8 size of std::io::Error: 8 size of std::num::ParseIntError: 1 size of serde_json::Error: 8 size of string: 24 size of MyError: 24 Error: Cannot find file: non_existent_file.txt Caused by: No such file or directory (os error 2)
Zusammenfassung
Die Tools zur Fehlerbehandlung von Rust haben jeweils ihren eigenen Schwerpunkt:
anyhow
ist einfach und effizient und eignet sich für die Anwendungsentwicklung.thiserror
ist strukturiert und klar und eignet sich für das Bibliotheksdesign.snafu
bietet einen umfangreichen Kontext und ist daher ideal für komplexe Szenarien.
Durch die Analyse und die praktischen Beispiele in diesem Artikel können Sie die geeignete Fehlerbehandlungslösung basierend auf Ihren tatsächlichen Bedürfnissen auswählen. Eine ordnungsgemäße Fehlerbehandlung macht Ihren Code robuster – üben Sie sie und steigern Sie die Qualität Ihrer Rust-Projekte!
Wir sind Leapcell, Ihre erste Wahl für das Hosten von Rust-Projekten.
Leapcell ist die Serverless-Plattform der nächsten Generation für Webhosting, asynchrone Aufgaben und Redis:
Mehrsprachige Unterstützung
- Entwickeln Sie mit Node.js, Python, Go oder Rust.
Stellen Sie unbegrenzt viele Projekte kostenlos bereit
- Zahlen Sie nur für die Nutzung – keine Anfragen, keine Gebühren.
Unschlagbare Kosteneffizienz
- Pay-as-you-go ohne Leerlaufgebühren.
- Beispiel: 25 USD unterstützen 6,94 Millionen Anfragen bei einer durchschnittlichen Reaktionszeit von 60 ms.
Optimierte Entwicklererfahrung
- Intuitive Benutzeroberfläche für mühelose Einrichtung.
- Vollautomatische CI/CD-Pipelines und GitOps-Integration.
- Echtzeitmetriken und -protokollierung für verwertbare Erkenntnisse.
Mühelose Skalierbarkeit und hohe Leistung
- Automatische Skalierung zur einfachen Bewältigung hoher Parallelität.
- Null Betriebsaufwand – konzentrieren Sie sich einfach auf das Erstellen.
Erfahren Sie mehr in der Dokumentation!
Folgen Sie uns auf X: @LeapcellHQ