Chapter 13: Box and Rust documentation
The introduction explains why Rust documentation is transferable to read, why Box matters, and how to recognize attributes.
After this chapter you will be able to
- Consistent Rust documentation format. Explain why becoming familiar with Rust documentation helps when reading documentation for other people’s code.
- Importance of Box. Describe Box as a smart pointer and explain, at the level given here, why the chapter calls it important.
- Consistent Rust documentation. Explain why learning to read Rust documentation helps when working with other people’s code and why that skill transfers across Rust projects.
- Equality assertions and panics. Explain that assert_eq! compares two items and panics if they are not equal; if an example runs without panicking, its asserted equality held.
- Assertions in documentation examples. Describe how to read assert_eq!(a, b) in documentation as a claim that a and b are the same at that point, and why examples use it instead of printed output.
- Documentation search shortcut. Explain how to search Rust documentation using the search bar and the S key after scrolling down a page.
- Source view in documentation. Explain why and when to click [src] in Rust documentation, using String::with_capacity() as an example.
- Trait required methods and deriving. Explain how to use the “Required Methods” list in trait documentation to see which methods you will probably need to write, and why Debug is usually handled with #[derive(Debug)] instead.
- Attribute placement and scope. Distinguish outer attributes written with # from inner attributes written with #!, including what each affects and where inner attributes belong.
- Derived trait limits. Explain what #[derive(TraitName)] does for a struct or enum and why the section's Display and Copy examples have limits.
- Box creation and storage. Explain that Box is a pointer type used to put a value’s data on the heap instead of the stack, and describe creating one with Box::new(item).
- Box ownership and access. Explain how a Box resembles a reference while owning its data, and describe how to access the value inside it.
- Recursive struct sizing. Explain why a Holder containing an optional Holder does not compile without Box, and how boxing the inner Holder fixes it.
- Boxed trait value sizing. Explain why a function cannot directly return a value described only by a trait when implementations may have different sizes, and why putting the value in a Box makes the return type usable.
- Dynamic versus static dispatch. Describe what dyn indicates for a boxed trait and distinguish dynamic dispatch from the static dispatch described for generics.
- Boxed errors and type erasure. Explain why Box<dyn Error> can be used when a function may return different error types, and describe what is hidden by the trait object.
- Concrete errors through downcasting. Explain why a Box<dyn Error> does not reveal which error type it contains and how downcasting can try to recover that concrete type.
What it builds on
- Cell (chapter 10) · Cell and its .get() method
- All you need are the method signatures (chapter 7) · Traits specify methods that an implementation must provide.
- Traits: The basics (chapter 7) · What it means for a type to implement a trait.
- Match statements (chapter 3) · Matching enum variants.
- The stack, the heap, pointers, and references (chapter 2) · Introduces the stack, heap, and pointers.
- Option (chapter 5) · Assumes familiarity with Some and None in Option.
- Traits as bounds (chapter 7) · A trait can describe types accepted through a trait bound.
- Regular generics compared to impl Trait (chapter 12) · The earlier comparison of generics and impl Trait.
Section by section
Introduction
The introduction explains why Rust documentation is transferable to read, why Box matters, and how to recognize attributes.
- Consistent Rust documentation format: Explain why becoming familiar with Rust documentation helps when reading documentation for other people’s code.
Why it matters: Learning one documentation format helps you explore code beyond this book. - Importance of Box: Describe Box as a smart pointer and explain, at the level given here, why the chapter calls it important.
Why it matters: This explains why Box deserves attention before the chapter teaches how to use it. - Attribute appearance and placement: Describe how the introduction says Rust attributes look and where they can appear.
Why it matters: Recognizing an attribute helps you identify it when reading Rust code.
Key terms: documentation, Box, smart pointer, traits, attributes, #[derive(Debug)]
13.1 Reading Rust documentation
This section explains why Rust documentation is useful to read and how to open documentation for your own code.
- Consistent Rust documentation: Explain why learning to read Rust documentation helps when working with other people’s code and why that skill transfers across Rust projects.
Why it matters: Once you are familiar with Rust documentation, you can use that familiarity to understand code written by others. - Opening local code documentation: Describe how the section says to generate and open documentation for Rust code on your computer.
Why it matters: This gives you a way to try reading documentation for your own code.
Key terms: Rust documentation, cargo doc, standard library
13.1.1 assert_eq!
Explains how to read assert_eq! in Rust documentation and why documentation examples use it.
- Equality assertions and panics: Explain that assert_eq! compares two items and panics if they are not equal; if an example runs without panicking, its asserted equality held.
Why it matters: This lets you tell what a documentation example is checking. - Assertions in documentation examples: Describe how to read assert_eq!(a, b) in documentation as a claim that a and b are the same at that point, and why examples use it instead of printed output.
Why it matters: You can understand the expected result of a Rust documentation example without needing printed output.
Key terms: assert_eq!, panic, println!, Display, Debug
13.1.2 Searching
This section shows how to search Rust documentation quickly, even after scrolling down a page.
- Documentation search shortcut: Explain how to search Rust documentation using the search bar and the S key after scrolling down a page.
Why it matters: The S key makes the search bar easy to reach when you are partway down a documentation page.
Key terms: search bar, S key
13.1.3 The [src] button
Shows when to open Rust documentation’s [src] view and how source code can reveal implementation details and method requirements.
- Source view in documentation: Explain why and when to click [src] in Rust documentation, using String::with_capacity() as an example.
Why it matters: You can start with the simpler documentation and inspect the implementation only when it helps. - Cell methods and Copy bounds: Explain how Cell’s source code makes the Copy requirement for methods such as .get() and .clone() visible.
Why it matters: Seeing the bound in the source gives a concrete reason for a method’s requirement.
Key terms: [src], source code, String, with_capacity(), Cell, Copy, impl<T: Copy>, .get(), .clone()
13.1.4 Information on traits
This section shows how to read a trait’s required methods in Rust documentation and when deriving an implementation can replace writing a method yourself.
- Trait required methods and deriving: Explain how to use the “Required Methods” list in trait documentation to see which methods you will probably need to write, and why Debug is usually handled with #[derive(Debug)] instead.
Why it matters: The required-method list helps you tell what implementing a trait will involve and when a derive attribute can do the work.
Key terms: Required Methods, Iterator, From, Debug, #[derive(Debug)]
13.1.5 Attributes
This section explains how Rust attributes affect code and compiler behavior, using common attributes for warnings, traits, configuration, and API changes.
- Attribute placement and scope: Distinguish outer attributes written with # from inner attributes written with #!, including what each affects and where inner attributes belong.
Why it matters: The prefix and placement tell you how broadly an attribute applies. - Suppressing unused code warnings: Explain why and when the section uses allow attributes for dead code and unused variables.
Why it matters: You can quiet distracting warnings without mistaking unused code for a compilation error. - Derived trait limits: Explain what #[derive(TraitName)] does for a struct or enum and why the section's Display and Copy examples have limits.
Why it matters: Derive saves work, but it cannot supply every trait implementation or override a field's capabilities. - Compiler configuration attributes: Describe the effects of the section's #[cfg(test)], target-OS cfg, and #![no_std] examples.
Why it matters: These attributes let code depend on the build context or omit the standard library. - Non-exhaustive enum matching: Explain what #[non_exhaustive] signals and how it affects matching an enum created by someone else.
Why it matters: The final match case allows for variants the enum's author may add later. - Deprecated items and warnings: Explain what marking an item #[deprecated] does and what a note can add.
Why it matters: Deprecation lets an API guide users toward changes without immediately breaking their code.
Key terms: attribute, outer attribute, inner attribute, #[allow(dead_code)], #[allow(unused_variables)], #![allow(unused)], #[derive(TraitName)], Display, Copy, Clone
13.2 Box
Introduces Box as a pointer type that puts a value’s data on the heap and shows how to create one.
- Box creation and storage: Explain that Box is a pointer type used to put a value’s data on the heap instead of the stack, and describe creating one with Box::new(item).
Why it matters: This gives you the basic meaning of Box before exploring what it is useful for.
Key terms: Box, pointer, heap, stack, Box::new()
13.2.1 Some Box basics
Introduces Box as an owning smart pointer and shows how its known size makes a recursive struct possible.
- Box ownership and access: Explain how a Box resembles a reference while owning its data, and describe how to access the value inside it.
Why it matters: This distinction helps explain why a Box can be used where an owning pointer is needed. - Recursive struct sizing: Explain why a Holder containing an optional Holder does not compile without Box, and how boxing the inner Holder fixes it.
Why it matters: It shows a concrete reason to use Box: making a recursive type whose size Rust can determine.
Key terms: Box, smart pointer, reference, *, recursive, Holder, Box::new(), Some, None
13.2.2 Putting a Box around traits
Explains why boxing a trait value lets a function return values of different sizes, and contrasts dynamic dispatch with the static dispatch used for generics.
- Boxed trait value sizing: Explain why a function cannot directly return a value described only by a trait when implementations may have different sizes, and why putting the value in a Box makes the return type usable.
Why it matters: This explains why boxing is useful when one return type must accommodate different trait implementations. - Dynamic versus static dispatch: Describe what dyn indicates for a boxed trait and distinguish dynamic dispatch from the static dispatch described for generics.
Why it matters: The distinction clarifies what changes when code uses a boxed trait instead of a generic type.
Key terms: Box, trait, size, dyn, dynamic dispatch, static dispatch, generics
13.2.3 Using a Box to handle multiple error types
This section shows why Box<dyn Error> can hold different error types, what information a trait object hides, and why recovering a concrete error type can matter.
- Requirements for custom error types: Explain that a type implementing std::error::Error must also implement Debug and Display, although Error itself has no required methods.
Why it matters: These requirements let an error provide information about what went wrong. - Boxed errors and type erasure: Explain why Box<dyn Error> can be used when a function may return different error types, and describe what is hidden by the trait object.
Why it matters: This explains the return type used when either of two operations could produce a different error. - Limits of printed errors: Explain why printing a trait-object error may not be enough and state what downcasting is meant to recover.
Why it matters: It clarifies why code might need more than an error’s displayed message.
Key terms: std::error::Error, Debug, Display, Box<dyn Error>, trait object, concrete type, type erasure, downcasting, dyn Error
13.2.4 Downcasting to a concrete type
This section shows how to recover a concrete error type from a boxed Error trait object by downcasting.
- Concrete errors through downcasting: Explain why a Box<dyn Error> does not reveal which error type it contains and how downcasting can try to recover that concrete type.
Why it matters: Downcasting lets you distinguish specific errors after handling them through a shared Error trait object.
Key terms: Box<dyn Error>, downcast, downcast_ref, concrete type
13.3 Summary
Where this is used later
- Crates and Cargo.toml (chapter 16) · Earlier practice reading Rust documentation helps with the familiar documentation layout.
- cargo doc (chapter 18) · That section returns to cargo doc in more detail.
- Just add #[test], and now it’s a test (chapter 14) · Understanding assertions helps when reading test examples.
- Checking the code inside external crates (chapter 17) · Finding a method’s source through documentation.
- Anyhow and thiserror (chapter 17) · The earlier alternative of using Box to handle error types.
- Further development and cleanup (chapter 23) · using `Box<dyn Error>` to handle errors
- Setting panic hooks (chapter 21) · The earlier discussion of downcasting provides context for trying to obtain a concrete type.
Concepts explained in this chapter
- BoxBox is Rust's owning smart pointer that stores a value on the heap while keeping a fixed-size pointer on the stack.
- attributeA struct field is a named or positional component of a Rust struct whose visibility can be declared independently of the struct type.
- derive macroA derive attribute is a Rust attribute that requests automatic implementation of specified traits for a type.
- downcastingDowncasting is the attempted conversion of a dynamically typed or trait-object value back to a specific concrete type.
- trait objectA trait object is a dynamically typed Rust value represented through a trait interface, allowing the concrete implementing type to be hidden.
- allow attributeAn allow attribute suppresses compiler diagnostics for specified lint conditions.
- conditional compilationA conditional compilation attribute instructs the Rust compiler to include or exclude an item according to a configuration condition.
- deprecationThe deprecated attribute marks a Rust item as discouraged for use and causes the compiler to issue a warning when it is used.
- inner attributeAn inner attribute is a Rust attribute written with #! that applies to the contents of its enclosing file or module.
- no_std programThe no_std attribute prevents a Rust program from linking the standard library by default.
- non_exhaustive attributeThe non_exhaustive attribute marks a Rust type as potentially gaining additional fields or enum variants in future versions.
- outer attributeAn outer attribute is a Rust attribute written with # that applies to the item immediately following it.
- smart pointerA smart pointer is a pointer-like data structure that manages an owned or referenced value and provides behavior beyond raw address storage.
- documentationDocumentation is explanatory material that describes how code works and how it can be used.
- ErrorError is the Rust standard trait that classifies error types and supports human-readable descriptions and error-source relationships.
- impl blockAn impl block is Rust syntax that defines methods or trait implementations associated with a type.
- 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.
- assert_eqassert_eq! is a Rust macro that panics when two expressions evaluate to unequal values and reports both values.
- cargo doccargo doc is the Cargo command-line tool that generates documentation for Rust packages and their dependencies, with --open optionally opening the generated documentation.
- DebugDebug is the Rust trait for producing programmer-oriented representations of values.
- traitA Rust trait is an abstract interface that defines capabilities or behavior a type can provide.
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.
