Debugging Rust¶
Level: 101 → 201 · working knowledge
One line: Most of what other languages debug at runtime, Rust declines to build — so the debugging left over is a smaller and stranger set of skills, and this page is the map to where each one is taught.
Debugging here is front-loaded. A use-after-free, a null dereference, a data race, an iterator invalidated mid-loop: nine of them are compile errors rather than an afternoon with a debugger. That moves the first skill from stepping through a running program to reading a message about one that never ran — which is why a section on debugging Rust has to start with the compiler rather than with a breakpoint.
What is genuinely left over splits three ways, and this section is the door to all three: printing a value, surviving a panic, and reading the refusal.
This is a map, not a place. Every page below lives in the section that owns it, the same way STRINGS.md indexes the strings arc. Nothing here is a second copy, and each link goes to the one page that is kept current.
Printing a value¶
The everyday tool, and the one with the most surprises in it.
| Page | What it answers |
|---|---|
Debug and Display |
Why {} refuses to print your struct and {:?} will, why the panic paths all reach for the generated trait rather than the sentence you wrote, and why a width on {:?} is accepted and then silently ignored |
What dbg! does |
The macro that captures the source text of the expression, hands the value back so you can wrap anything in place, and writes to stderr — which is where half of the surprise lives |
str::escape_debug |
Seeing the character that is present and invisible: a tab, a trailing space, a zero-width space |
When it panics¶
| Page | What it answers |
|---|---|
unwrap is a TODO you forgot to remove |
Where the unwrap you are now debugging came from, and why nothing in the default toolchain ever asked about it |
expect: writing down the proof |
The sentence is a claim about why this could not fail — and failing to write it is the finding |
| What a panic costs | Unwinding is tidy about memory and careless about work: every destructor runs, and everything unfinished simply never happens |
| Reading a backtrace | The frame list the panic message leaves out — why it is off by default, why the caller you want is below the panic site, and why an optimised build prints half of it |
| What a test asserts | A test is a function that panics when it is unhappy — the shortest route from "it broke once" to "it cannot break again" |
When it will not build¶
| Page | What it answers |
|---|---|
| Reading a compilation failure | Which of four programs produced your message — parser, type checker, borrow checker, linker — because the fix is different in each |
| "No method named …" | One error code over four unrelated bugs, and the help: line that separates them — including the case where the trait to implement is not the trait the method came from |
| C and C++: the bugs Rust is a reply to | Nine runtime bugs, each watched happening in C and then refused by rustc. The best argument for reading errors instead of running debuggers |
Once somebody else depends on it¶
| Page | What it answers |
|---|---|
| A log line a machine can read | Printing stops scaling the moment the question is about one request out of a million |
What is not here yet¶
Two gaps, named rather than quietly left out:
- A real debugger.
rust-gdbandrust-lldbship with the toolchain — they are in$(rustc --print sysroot)/binalongsiderustcandcargo, so they are already installed — and no page in this library uses one. They are wrappers that load the pretty-printers, without which aStringshows as its raw parts. tracingand friends. Observability is the section for this and says honestly why its pages are the hardest here to finish: every checked example in this library compiles withrustcalone, and that is a crate story.
A page graduates out of that list the way every page here arrives — with a program CI compiles, runs, and diffs against a recorded answer key. CONTRIBUTING.md says how.
Po polsku¶
Ta strona jest mapą, nie lekcją, a jej teza jest dla uczącego się wygodna: większość tego, co w innych językach debuguje się w czasie działania, Rust po prostu odmawia zbudować. Wartość pusta, wyścig danych, użycie po zwolnieniu, zapomniany wariant w match — gdzie indziej bywają sesją przy debuggerze, tutaj są komunikatem kompilatora.
Skutek jest taki, że debugowanie w Ruscie to mniejszy i dziwniejszy zestaw umiejętności niż gdzie indziej, i dzieli się mniej więcej tak: jak wypisać wartość (dbg!, {:?}, {:#?}), co robić, gdy panikuje (ślad stosu, RUST_BACKTRACE=1), co robić, gdy się nie buduje — a to przypadek najczęstszy i najmniej podobny do debugowania w klasycznym sensie — i wreszcie co robić, gdy ktoś już od twojego kodu zależy.
Jedna uwaga o oczekiwaniach. Krokowanie debuggerem jest w Ruscie znacznie rzadsze niż w C++ czy w Javie, i nie z braku narzędzi — rust-gdb i rust-lldb są w zestawie — tylko dlatego, że pytania, na które debugger odpowiada, zwykle padają wcześniej, przy kompilacji. Kto szuka polskich materiałów o debugowaniu Rusta i znajduje ich mało, niczego nie przeoczył: po angielsku jest ich niewiele więcej, z tego samego powodu.
Szukaj po polsku: debugowanie Rusta · dbg! i RUST_BACKTRACE · czytanie komunikatów kompilatora · rust debugging gdb lldb