Debugging von Rust-Code: von «println!» bis zu Profi-Werkzeugen
Jeder, der mindestens eine Woche Rust geschrieben hat, kennt dieses Gefühl: Der Compiler hat durchgelassen, das Programm wurde kompiliert, startet … und macht etwas völlig Unerwartetes. Oder stürzt einfach mit einem rätselhaften Segmentation fault ab. Willkommen in der Welt des Debuggings! Die gute Nachricht: Rust bietet dafür ein ganzes Arsenal – vom einfachen Makro bis zu fortgeschrittenen Debuggern. In diesem Artikel besprechen wir alle Methoden des Debugging von Rust-Code, von den grundlegenden bis zu den professionellen.
1. Das Grundlagen-Set: Die Makros dbg! und eprintln!
Beginnen wir mit dem Einfachsten und Zugänglichsten. Wenn Sie Rust-Code schreiben und schnell verstehen möchten, was in Variablen passiert – verwenden Sie dbg!. Es gibt sowohl den Variablennamen als auch seinen Wert sowie die Datei und Zeile aus. Viel bequemer als das gute alte println!.
fn calculate_even_sum(numbers: &[i32]) -> i32 { let mut sum = 0; for &n in numbers { if n % 2 == 0 { sum += n; dbg!(n, sum); // sehen wir uns jede gerade Zahl und die aktuelle Summe an } } sum}
fn main() { let data = vec![1, 2, 3, 4, 5, 6]; let result = calculate_even_sum(&data); dbg!(result);}Starten Sie es – und Sie werden so etwas sehen:
[src/main.rs:6:13] n = 2[src/main.rs:6:13] sum = 2[src/main.rs:6:13] n = 4[src/main.rs:6:13] sum = 6[src/main.rs:6:13] n = 6[src/main.rs:6:13] sum = 12[src/main.rs:12:5] result = 12Und wenn Sie in stderr schreiben müssen (z. B. um stdout bei Pipes nicht zu stören) – verwenden Sie eprintln!. Dies ist unverzichtbar beim Debugging von Rust-Code in einer Produktionsumgebung.
2. Fortgeschrittenes Logging mit log und env_logger
Wenn das Projekt wächst, wird es mühsam, überall dbg! zu verteilen. Hier helfen die Crates log und env_logger. Sie ermöglichen es, Logs ohne Neukompilierung ein- und auszuschalten.
use log::{info, warn, error, debug};
fn main() { env_logger::init();
info!("Programm gestartet"); let x = 42; debug!("Wert von x: {}", x);
if x > 100 { warn!("x ist zu groß!"); } else { error!("Das ist kein Fehler, nur ein Test"); }}Starten Sie mit der Umgebungsvariable RUST_LOG=debug cargo run – und Sie erhalten alle Logs. Möchten Sie nur Warnungen? RUST_LOG=warn. Sehr praktisch für das Debugging von Rust-Anwendungen in Echtzeit.
3. Panik und Backtrace: Den Fehlerstapel lesen
Wenn ein Programm in Panik gerät, gibt Rust eine Meldung aus. Aber oft reicht das nicht. Aktivieren Sie den vollständigen Backtrace:
$ RUST_BACKTRACE=1 cargo runOder für maximale Detailtiefe:
$ RUST_BACKTRACE=full cargo runSie sehen den gesamten Aufrufstapel – vom Absturzpunkt bis zu main. Dies ist entscheidend beim Debugging von Rust-Code, wenn Sie nicht verstehen, woher der Fehler kommt.
Beispiel: Angenommen, Sie haben diesen Code:
fn main() { let v = vec![1, 2, 3]; println!("{}", v[10]); // Panik!}Mit RUST_BACKTRACE=1 sehen Sie nicht nur "index out of bounds", sondern die genaue Zeile und den Aufruf.
4. Debug-Modus und Optimierungen: debug vs release
Ein wichtiger Punkt: Das Debugging von Rust-Code im debug-Modus (Standard) liefert vollständige Informationen. Aber im release-Modus optimiert der Compiler den Code, und einige Variablen können verschwinden oder ihre Reihenfolge ändern. Wenn Sie einen "Release"-Bug debuggen – verwenden Sie ein Profil mit Debug-Informationen:
[profile.release]debug = trueIn Cargo.toml. Dies ermöglicht Ihnen, cargo run --release
Blogs
Buchempfehlungen
Lesen Sie auch
Wie man in 30 Minuten eine REST API in C# schreibt: Schritt-für-Schritt-Anleitung
Testen in Kotlin: Unit-, Integrations- und E2E-Tests von A bis Z
Profilierung von C#-Code: Engpässe für Anfänger finden
Bereitstellung von Rust auf Kubernetes: Ein grundlegender Leitfaden für Anfänger
TypeScript-Fehler string | undefined: Wie man ihn behebt und nicht verrückt wird