Chapter 21: Continuing the tour
Introduces the second standard-library tour and previews its topics, including macros.
After this chapter you will be able to
- Swapping Variable Values. Describe how swap() exchanges two values and what arguments it takes.
- Replacing And Returning Values. Explain how replace() changes a value while giving the caller its previous value.
- Taking Values With Defaults. Explain what take() returns, what it leaves behind, and why the value's type must implement Default.
- Default panic behavior. Describe what happens during an ordinary panic and what the default message reports.
- Custom panic hook behavior. Explain how set_hook changes panic reporting and how its closure can access panic information.
- Panic payload inspection safety. Describe how the section inspects a panic payload and why the hook must handle an unsuccessful downcast safely.
- Runtime backtrace capture. Explain how to capture a backtrace at run time with std::backtrace::Backtrace::capture(), and why the relevant environment variable affects what it displays.
- Standard library prelude. Explain that the prelude lets Rust code use common items such as `Vec` and `Ok` without writing their full paths, and describe what the `#![no_implicit_prelude]` example demonstrates.
- Unreachable macro and its risk. Explain that unreachable! marks a branch the programmer is certain will never run, including a case the compiler cannot establish on its own. State that reaching the macro causes a panic, so the programmer must be sure the branch is unreachable.
- Unreachable match pattern warnings. Distinguish the compiler’s unreachable-pattern warning from the unreachable! macro, and explain how writing summer instead of Summer makes later match arms unreachable.
- Source location macros. Explain what column!, line!, file!, and module_path! report, and describe how their output can help locate code that needs attention.
- Independent thread-local values. Explain that a static created with thread_local! starts with the same initial value in each thread, but changes in one thread do not affect the others. Contrast this with the shared global value in the example.
- Configuration checks with cfg!. Explain that cfg! returns a bool for a configuration condition, letting code change a small part of its behavior depending on the configuration.
What it builds on
- Copy types (chapter 2) · The distinction between Copy values and other values.
- Mutable references (chapter 2) · Using mutable references.
- Implementing Default (chapter 15) · The Default trait and default values.
- Option (chapter 5) · The Some and None variants of Option.
- Result (chapter 5) · Earlier treatment of Result provides context for the example that panics after an error is unwrapped.
- Closures and closures inside iterators (chapter 8) · Closures provide the form used to define the hook's behavior.
- Downcasting to a concrete type (chapter 13) · The earlier discussion of downcasting provides context for trying to obtain a concrete type.
- Crates and Cargo.toml (chapter 16) · Cargo.toml is introduced as the place for project configuration.
Section by section
Introduction
Introduces the second standard-library tour and previews its topics, including macros.
21.1 std::mem
The section introduces useful std::mem functions and explains how drop, swap, replace, and take affect values.
- Dropping Copy Values: Explain why calling drop() on a Copy value does not make the original value disappear.
Why it matters: It explains why drop() behaves differently from what you might expect for an integer. - Swapping Variable Values: Describe how swap() exchanges two values and what arguments it takes.
Why it matters: swap() provides a direct way to exchange values when doing so yourself is awkward under borrowing rules. - Replacing And Returning Values: Explain how replace() changes a value while giving the caller its previous value.
Why it matters: It lets you keep the old value while putting a new one in its place. - Taking Values With Defaults: Explain what take() returns, what it leaves behind, and why the value's type must implement Default.
Why it matters: take() lets you retrieve a value without leaving its original place empty.
Key terms: drop(), Copy, std::mem, swap(), mutable reference, replace(), let binding, take(), Default, Some
21.2 Setting panic hooks
This section explains how Rust handles panics, how to customize panic reporting with hooks, and why hook code must avoid panicking.
- Default panic behavior: Describe what happens during an ordinary panic and what the default message reports.
Why it matters: The default behavior provides the baseline for understanding what a custom hook changes. - Custom panic hook behavior: Explain how set_hook changes panic reporting and how its closure can access panic information.
Why it matters: A custom hook lets a program choose what happens when a panic is reported, rather than relying on the default message. - Panic payload inspection safety: Describe how the section inspects a panic payload and why the hook must handle an unsuccessful downcast safely.
Why it matters: Defensive handling matters especially in a hook, where another panic changes the outcome from unwinding to aborting. - Intentional abort tradeoff: Explain why someone might configure aborting on panic and distinguish that choice from panicking inside a hook.
Why it matters: It distinguishes a deliberate binary-size tradeoff from an accidental abort caused by faulty hook code. - Pre-unwind database shutdown: Describe the purpose of the database example's custom panic hook.
Why it matters: The example shows a reason to use a hook beyond changing the text of a panic message. - Panic hook removal: Explain what take_hook does after a custom panic hook has been installed.
Why it matters: Removing a custom hook restores ordinary reporting and illustrates a use of mem::take.
Key terms: panic, unwinds the stack, main thread, backtrace, std::panic, set_hook(), global panic hook, closure, PanicInfo, payload
21.3 Viewing backtraces
This section shows how to capture a backtrace at run time, enable it with an environment variable, inspect its status, and decide when it may be useful.
- Runtime backtrace capture: Explain how to capture a backtrace at run time with std::backtrace::Backtrace::capture(), and why the relevant environment variable affects what it displays.
Why it matters: Enabling capture lets you request a backtrace while the program runs, without adding a crate. - Backtrace status matching: Describe what Backtrace::status() returns and why a match on its result must account for possible additional variants.
Why it matters: Checking status gives the program a way to respond to backtrace support rather than merely printing the Backtrace. - Backtrace diagnostic value: Explain why backtraces may be less necessary in Rust than in some other languages while remaining useful.
Why it matters: This puts backtraces in perspective as an optional source of diagnostic detail.
Key terms: std::backtrace, Backtrace::capture(), RUST_BACKTRACE, RUST_LIB_BACKTRACE, Backtrace::status(), BacktraceStatus, #[non_exhaustive], Unsupported, backtraces, stack traces
21.4 The standard library prelude
This section shows how the standard library prelude makes common Rust items available automatically and explains why older code may contain `extern crate`.
- Standard library prelude: Explain that the prelude lets Rust code use common items such as `Vec` and `Ok` without writing their full paths, and describe what the `#![no_implicit_prelude]` example demonstrates.
Why it matters: It explains why familiar Rust names work without imports and why disabling the prelude makes simple code cumbersome. - Older extern crate syntax: Distinguish the older need for `extern crate` from the usual modern practice of using `use` to bring items into scope.
Why it matters: It helps you recognize `extern crate` in older Rust code without assuming you normally need to write it.
Key terms: prelude, #![no_implicit_prelude], Vec, Ok, extern crate, use, external crate
21.5 Other macros
Introduces a look at more standard-library macros before the next chapter turns to writing macros.
21.5.1 unreachable!
This section explains when to use Rust’s unreachable! macro and how an unreachable-pattern warning can reveal a mistake in a match.
- Unreachable macro and its risk: Explain that unreachable! marks a branch the programmer is certain will never run, including a case the compiler cannot establish on its own. State that reaching the macro causes a panic, so the programmer must be sure the branch is unreachable.
Why it matters: The macro communicates an assumption to readers, but a wrong assumption will cause a panic. - Unreachable match pattern warnings: Distinguish the compiler’s unreachable-pattern warning from the unreachable! macro, and explain how writing summer instead of Summer makes later match arms unreachable.
Why it matters: An unreachable-pattern warning can point to a typo that changes what a match arm accepts.
Key terms: unreachable!, todo!, panic, unreachable pattern, summer, match
21.5.2 column!, line!, file!, and module_path!
This section introduces four macros that report where they are called in the code and shows why that location can be useful in a warning.
- Source location macros: Explain what column!, line!, file!, and module_path! report, and describe how their output can help locate code that needs attention.
Why it matters: A warning is more useful when it points you to the code you need to inspect.
Key terms: column!, line!, file!, module_path!
21.5.3 thread_local!
This section explains how thread_local! creates a separately initialized static value for each thread and how code accesses that value.
- Independent thread-local values: Explain that a static created with thread_local! starts with the same initial value in each thread, but changes in one thread do not affect the others. Contrast this with the shared global value in the example.
Why it matters: It explains why separate threads can start with the same value without changing one another’s copy. - Thread-local value access: Describe how .with() provides access to the value created by thread_local! inside a closure.
Why it matters: It identifies the access pattern used with a thread-local value.
Key terms: thread_local!, lazy_static!, LOCAL_INITIAL_VALUE, INITIAL_VALUE, .with(), closure, LocalKey
21.5.4 cfg!
The section shows how cfg! returns a boolean for configuration-dependent behavior, using a function that handles real files during a normal run and dummy data during a test.
- Configuration checks with cfg!: Explain that cfg! returns a bool for a configuration condition, letting code change a small part of its behavior depending on the configuration.
Why it matters: This lets you recognize when a configuration check can be used within ordinary code. - Test-dependent file data: Describe how the example uses the test configuration to choose between opening main.rs during a normal run and creating a dummy String during a test.
Why it matters: The example makes the effect of a configuration check visible in normal runs and tests.
Key terms: cfg!, configuration, bool, attributes, UserFile, File, String, open_file(), cargo run, cargo test
21.6 Summary
Concepts explained in this chapter
- panic hookA panic hook is a globally registered callback invoked when a Rust panic occurs before the runtime performs its panic strategy.
- PanicInfoPanicInfo is a Rust structure containing contextual information about a panic, including its location and payload.
- downcast_refdowncast_ref is a method on an Error trait object that attempts to obtain a shared reference to the value as a specified concrete error type.
- set_hookset_hook is a Rust standard-library function that installs a global panic hook.
- std::panicstd::panic is the Rust standard-library module that provides panic hooks and related panic-handling facilities.
- stack traceA backtrace is a record of the call stack showing the sequence of function calls leading to a program point or failure.
- DefaultDefault is the Rust standard trait that defines how to construct a default value for a type.
- macroA Rust macro generates or transforms source code during compilation.
- anyAny is a Rust standard-library trait for values that can support runtime type inspection and downcasting when they satisfy the required lifetime conditions.
- cargo.tomlCargo.toml is the manifest file that declares a Rust package's metadata and dependencies, including external crates and their versions.
- attributeA struct field is a named or positional component of a Rust struct whose visibility can be declared independently of the struct type.
These guides are generated from the text of Manning books by Manning's knowledge graph and link to the sections they draw on. The full books are available on liveBook and at manning.com.
