Rust Fehlerbehandlung

6 Kernkonzepte
Die wichtigsten Konzepte zur Fehlerbehandlung in Rust: Result · Option · unwrap · ? Operator · panic! · anyhow / thiserror Rust unterscheidet zwischen behebbaren und unbehebbaren Fehlern. Die Fehlerbehandlung ist explizit und typsicher – das macht Rust-Programme robust und zuverlässig.

Result<T, E> – Behebbare Fehler

Ok(T) · Err(E) · match · ?
// Result-Definition enum Result<T, E> { Ok(T), Err(E), } // Verwendung fn divide(a: f64, b: f64) -> Result<f64, String> { if b == 0.0 { Err("Division durch Null".to_string()) } else { Ok(a / b) } }

Result<T, E> ist der zentrale Typ für behebbare Fehler in Rust. Er kann entweder einen erfolgreichen Wert (Ok(T)) oder einen Fehler (Err(E)) enthalten. Die Fehlerbehandlung ist explizit und typsicher.

Beispiele
// Funktion mit Result
use std::fs::File;
use std::io::Read;
fn read_file(path: &str) -> Result<String, std::io::Error> {
let mut file = File::open(path)?;
let mut content = String::new();
file.read_to_string(&mut content)?;
Ok(content)
}
// match für Fehlerbehandlung
let result = divide(10.0, 0.0);
match result {
Ok(value) => println!("Ergebnis: {}", value),
Err(e) => println!("Fehler: {}", e),
}
// if let für einen Fall
if let Ok(value) = divide(10.0, 2.0) {
println!("Ergebnis: {}", value);
}
Tipp: Result zwingt Sie, Fehler explizit zu behandeln. Das macht Rust-Code robust und vorhersagbar. Verwenden Sie match oder den ?-Operator für elegante Fehlerweitergabe.

Option<T> – Optionaler Wert

Some(T) · None · unwrap_or
// Option-Definition enum Option<T> { Some(T), None, } // Verwendung fn find_user(id: u32) -> Option<String> { if id == 1 { Some("Anna".to_string()) } else { None } }

Option<T> repräsentiert einen Wert, der vorhanden sein kann (Some(T)) oder nicht (None). Er wird verwendet, wenn ein Fehler nicht die richtige Semantik hat (z.B. "Wert nicht gefunden").

Beispiele
// Option mit match
let name = find_user(1);
match name {
Some(n) => println!("Name: {}", n),
None => println!("Benutzer nicht gefunden"),
}
// Option mit unwrap_or (Default-Wert)
let name = find_user(5).unwrap_or("Unbekannt".to_string());
println!("Name: {}", name);
// if let für Option
if let Some(n) = find_user(2) {
println!("Gefunden: {}", n);
}
// Option in Collections
let vec = vec![1, 2, 3];
let first = vec.get(0); // Some(&1)
let none = vec.get(10); // None
Tipp: Option wird oft für fehlende Werte verwendet (z.B. bei HashMap::get() oder Vec::get()). unwrap_or() und unwrap_or_else() sind praktisch für Default-Werte.

unwrap & expect – Panic bei Fehlern

unwrap() · expect() · unwrap_or()
// unwrap – panicked bei Err/None let x = some_result.unwrap(); // expect – mit eigener Fehlermeldung let x = some_result.expect("Fehlermeldung"); // unwrap_or – Default-Wert bei Fehler let x = some_result.unwrap_or(0);

unwrap() und expect() entpacken Result oder Option – bei Err oder None wird ein panic! ausgelöst. Sie sollten nur verwendet werden, wenn Sie sicher sind, dass kein Fehler auftritt.

Beispiele
// unwrap – einfach aber gefährlich
let file = File::open("config.txt").unwrap();
// expect – bessere Fehlermeldung
let file = File::open("config.txt")
.expect("Konfigurationsdatei config.txt konnte nicht geöffnet werden");
// unwrap_or – Default-Wert bei Fehler
let port = env::var("PORT")
.unwrap_or("8080".to_string());
// unwrap_or_else – mit Closure für Default
let value = some_option.unwrap_or_else(|| {
println!("Berechne Default-Wert...");
42
});
// unwrap in Tests (okay)
#[test]
fn test_add() {
assert_eq!(add(2, 3).unwrap(), 5);
}
⚠️ Warnung: unwrap() und expect() sollten in Produktionscode vermieden werden, es sei denn, Sie sind absolut sicher, dass kein Fehler auftritt. Verwenden Sie stattdessen den ?-Operator oder explizite Fehlerbehandlung mit match.

? Operator – Fehler elegant weitergeben

? · Result · Option
// ? Operator für Result fn read_file() -> Result<String, std::io::Error> { let mut file = File::open("data.txt")?; // Fehler wird weitergereicht let mut content = String::new(); file.read_to_string(&mut content)?; Ok(content) } // ? Operator für Option fn get_first(list: &[i32]) -> Option<i32> { let first = list.first()?; Some(*first) }

Der ? Operator ist eine Abkürzung für die Fehlerweitergabe. Bei Result gibt er bei Err den Fehler zurück, bei Ok wird der Wert entpackt. Bei Option gibt er bei None None zurück.

Beispiele
// ? mit Result in main()
use std::fs::File;
use std::io::Read;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut file = File::open("config.toml")?;
let mut content = String::new();
file.read_to_string(&mut content)?;
println!("Inhalt: {}", content);
Ok(())
}
// ? mit Option in einer Funktion
fn get_name(id: u32) -> Option<String> {
let user = db.get(id)?; // None wird zurückgegeben
Some(user.name.clone())
}
// ? in einer Chain (mehrere Operationen)
fn process_data() -> Result<String, std::io::Error> {
let data = File::open("data.bin")?
.read_to_string()?;
Ok(data)
}
Tipp: Der ?-Operator kann nur in Funktionen verwendet werden, die Result oder Option zurückgeben. In main() können Sie Result<(), Box<dyn Error>> als Rückgabetyp verwenden.

panic! – Unbehebbare Fehler

panic!() · unwrap · expect
// panic! Makro panic!("Ein kritischer Fehler ist aufgetreten"); // Mit Formatierung panic!("Ungültiger Wert: {}", wert);

panic! wird für unbehebbare Fehler verwendet, die nicht sinnvoll behandelt werden können. Es beendet das Programm und gibt eine Fehlermeldung aus. unwrap() und expect() rufen intern panic! auf.

Beispiele
// panic! bei ungültigem Index
fn get_element(vec: &[i32], index: usize) -> i32 {
if index >= vec.len() {
panic!("Index {} außerhalb der gültigen Bereich (Länge: {})", index, vec.len());
}
vec[index]
}
// panic! in Tests
#[test]
fn test_invalid_index() {
let vec = vec![1, 2];
let result = std::panic::catch_unwind(|| {
get_element(&vec, 5);
});
assert!(result.is_err());
}
// Panic in der Praxis
fn main() {
let value = -5;
if value < 0 {
panic!("Wert darf nicht negativ sein: {}", value);
}
}
Tipp: Verwenden Sie panic! nur für kritische Fehler, die nicht behoben werden können (Programmfehler, invariante Verletzungen). Für erwartbare Fehler (Benutzereingabe, Dateioperationen) verwenden Sie Result.

anyhow & thiserror – Fehlerbehandlung in der Praxis

anyhow · thiserror · Cargo
// anyhow – für einfache Fehlerweitergabe use anyhow::{Result, bail}; fn main() -> Result<()> { bail!("Ein Fehler ist aufgetreten"); Ok(()) } // thiserror – für benutzerdefinierte Fehlertypen use thiserror::Error; #[derive(Error, Debug)] pub enum MyError { #[error("IO-Fehler: {0}")] Io(std::io::Error), }

anyhow und thiserror sind zwei populäre Crates für die Fehlerbehandlung. anyhow vereinfacht die Fehlerweitergabe, thiserror hilft bei der Definition eigener Fehlertypen.

Crates im Vergleich

Crate Verwendung Vorteil
anyhow Anwendungen, CLI-Tools Einfache Fehlerweitergabe, automatische Konvertierung
thiserror Bibliotheken, APIs Strukturierte Fehlertypen, `std::error::Error`-Implementierung
Beispiele
// anyhow – einfache Fehlerbehandlung
use anyhow::{Context, Result};
fn read_config() -> Result<String> {
let data = std::fs::read_to_string("config.toml")
.context("Konfigurationsdatei konnte nicht gelesen werden")?;
Ok(data)
}
// thiserror – benutzerdefinierter Fehlertyp
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ConfigError {
#[error("Datei nicht gefunden: {0}")]
NotFound(String),
#[error("Ungültiges Format: {0}")]
InvalidFormat(String),
#[error("IO-Fehler: {0}")]
Io(std::io::Error),
}
// Konvertierung von std::io::Error
impl From<std::io::Error> for ConfigError {
fn from(err: std::io::Error) -> Self {
ConfigError::Io(err)
}
}
// Verwendung in main()
fn main() -> Result<(), ConfigError> {
let content = std::fs::read_to_string("config.toml")?;
println!("{}", content);
Ok(())
}
Tipp: Für Anwendungen und CLI-Tools ist anyhow die richtige Wahl. Für Bibliotheken verwenden Sie thiserror, um Ihren Nutzern strukturierte Fehlertypen zu bieten.

Rust Fehlerbehandlung im Überblick

Result Behebbare Fehler
Ok / Err
Option Optionaler Wert
Some / None
? Fehler weitergeben
Elegant und kurz
unwrap Panic bei Fehler
Vorsichtig verwenden
panic! Unbehebbare Fehler
Programm beenden
anyhow Einfache Fehlerbehandlung
Für Anwendungen

Quick Summary

Result
Behebbare Fehler
Option
Optionaler Wert
unwrap
Panic bei Fehler
?
Fehler weitergeben
panic!
Unbehebbare Fehler
anyhow
Fehler-Crates
fn read_file() -> Result<String, io::Error> { let mut f = File::open("file.txt")?; ... }