Chapter 6: More collections, more error handling
This chapter introduction previews additional Rust collections and later discussions of the ? operator, panic, and unwrap.
After this chapter you will be able to
- Accessing other collection types. Explain that Rust’s additional collection types are in the standard library’s std::collections module and that a use statement brings them into scope.
- HashMap Keys and Values. Explain that a HashMap stores keys paired with values, and describe how to create one and insert a pair.
- HashMap and BTreeMap Ordering. Distinguish the order in which HashMap and BTreeMap present their keys, and explain why the example can switch between them with little code change.
- Safe HashMap Lookups. Distinguish square-bracket lookup from .get() when a key might be absent.
- Existing Key Insertion. Explain what .insert() does when a key already exists, how its return value preserves the old value, and how checking first can avoid replacement.
- Entry Defaults and Updates. Trace how .entry(key).or_insert(default) either uses an existing value or inserts a default, then returns a mutable reference that can update the map.
- HashSet membership and uniqueness. Explain why a HashSet is useful for checking whether a value is present and what happens when repeated numbers are put into one.
- HashSet versus BTreeSet ordering. Distinguish the unpredictable printed order of a HashSet from the ordered values of a BTreeSet, and describe how the section checks that order.
- BinaryHeap ordering. Explain that a BinaryHeap keeps its greatest item at the front without sorting the remaining items.
- Front removal in a Vec. Explain why repeatedly removing the first item of a Vec can be slow, in contrast to popping its last item.
- VecDeque for both ends. Describe when to choose a VecDeque over a Vec and how the section converts a Vec into one.
- Question mark Result behavior. Explain what `?` does after an expression returning a `Result`, and relate it to a shorter `match`.
- Parsing function control flow. Trace the section’s parsing function through successful and failed parsing, including why its final number is wrapped in `Ok`.
- Early return from main. Explain when `?` can be used in `main()` and why the section recommends it only when ending the program early on an error is acceptable.
- Panics for logic bugs. Explain why a deliberate panic can reveal a logic bug that would otherwise go unnoticed, using the three-item vector example.
- Unwrap and expect tradeoffs. Explain why unwrap can be useful while first writing a program, why it is risky in finished code, and what expect improves without preventing a panic.
What it builds on
- Collection types (chapter 3) · Introduces collection types.
- Option (chapter 5) · Option and its Some and None variants.
- Mutable references (chapter 2) · Using a mutable reference to change a value.
- Vectors (chapter 3) · Storing multiple items in a Vec.
- Tuples (chapter 3) · Tasks are represented as pairs of an importance number and a description.
- Result (chapter 5) · The `Ok` and `Err` cases of `Result`.
Section by section
Introduction
This chapter introduction previews additional Rust collections and later discussions of the ? operator, panic, and unwrap.
6.1 Other collections
Introduces where Rust’s other collection types are found and how to bring them into scope.
- Accessing other collection types: Explain that Rust’s additional collection types are in the standard library’s std::collections module and that a use statement brings them into scope.
Why it matters: This tells you where to find additional collections and how to make them available in your code.
Key terms: std::collections, standard library, use statement
6.1.1 HashMap (and BTreeMap)
This section introduces HashMap key–value storage, contrasts its key order with BTreeMap, and shows ways to look up, replace, and update values.
- HashMap Keys and Values: Explain that a HashMap stores keys paired with values, and describe how to create one and insert a pair.
Why it matters: Key–value pairs are the basis for every example in this section. - HashMap and BTreeMap Ordering: Distinguish the order in which HashMap and BTreeMap present their keys, and explain why the example can switch between them with little code change.
Why it matters: It explains both why printed map output can vary and which collection to choose when key order matters. - Safe HashMap Lookups: Distinguish square-bracket lookup from .get() when a key might be absent.
Why it matters: Choosing .get() lets code handle an absent key without crashing. - Existing Key Insertion: Explain what .insert() does when a key already exists, how its return value preserves the old value, and how checking first can avoid replacement.
Why it matters: It helps you decide whether to replace a value, keep it, or save what was replaced. - Entry Defaults and Updates: Trace how .entry(key).or_insert(default) either uses an existing value or inserts a default, then returns a mutable reference that can update the map.
Why it matters: The entry pattern lets repeated keys update a count or accumulate items without replacing the existing value.
Key terms: HashMap, key, value, HashMap::new(), .insert(), BTreeMap, keys, ordered, square brackets, .get()
6.1.2 HashSet and BTreeSet
This section explains how sets keep distinct values and how BTreeSet provides ordering that HashSet does not.
- HashSet membership and uniqueness: Explain why a HashSet is useful for checking whether a value is present and what happens when repeated numbers are put into one.
Why it matters: A set is useful when you care which values appeared, not how many times each appeared. - HashSet versus BTreeSet ordering: Distinguish the unpredictable printed order of a HashSet from the ordered values of a BTreeSet, and describe how the section checks that order.
Why it matters: Choosing between these sets depends in part on whether you need the values in order.
Key terms: HashSet, HashMap, keys, BTreeSet, ordering
6.1.3 BinaryHeap
This section introduces BinaryHeap as a collection that puts its greatest item first and illustrates its use for prioritizing tasks.
- BinaryHeap ordering: Explain that a BinaryHeap keeps its greatest item at the front without sorting the remaining items.
Why it matters: The heap’s ordering tells you what to expect first without assuming the whole collection is sorted. - Task priority example: Describe how the section uses a BinaryHeap of tasks, each with an importance number and a description, so the greatest item appears first.
Why it matters: This example shows why putting the greatest item first is useful for a to-do list.
Key terms: BinaryHeap, priority queue, BinaryHeap<(u8, &str)>, u8, &str
6.1.4 VecDeque
Explains why removing items from the front of a Vec is costly and when a VecDeque is a better choice.
- Front removal in a Vec: Explain why repeatedly removing the first item of a Vec can be slow, in contrast to popping its last item.
Why it matters: It explains why code that repeatedly removes the first Vec item can become unexpectedly slow. - VecDeque for both ends: Describe when to choose a VecDeque over a Vec and how the section converts a Vec into one.
Why it matters: It gives you a practical way to choose a collection when your code works at both ends.
Key terms: Vec, .pop(), .remove(), remove(0), VecDeque, ring buffer, VecDeque::from()
6.2 The ? operator
This section explains how `?` handles `Result` by extracting successful values or returning errors early, then shows where that behavior is useful and where differing error types limit it.
- Question mark Result behavior: Explain what `?` does after an expression returning a `Result`, and relate it to a shorter `match`.
Why it matters: This lets you read code that handles errors without an explicit `match` at each step. - Parsing function control flow: Trace the section’s parsing function through successful and failed parsing, including why its final number is wrapped in `Ok`.
Why it matters: Following the two paths shows why code after `?` can use the parsed value and assume that step succeeded. - Early return from main: Explain when `?` can be used in `main()` and why the section recommends it only when ending the program early on an error is acceptable.
Why it matters: A startup failure may be a good reason to stop the entire program rather than continue. - Single error type limit: Explain why the section cannot yet use `?` for both byte-to-String conversion and number parsing in one function.
Why it matters: It explains why adding `?` to each fallible step is not enough when those steps produce different error types.
Key terms: ? operator, Result, Ok, Err, early return, match, .parse(), ParseIntError, Result<i32, ParseIntError>, main()
6.3 When panic and unwrap are good
This section explains when deliberate panics help catch mistakes, how assertions and expect improve failure messages, and how unwrap_or avoids a panic by supplying a fallback value.
- Panics for logic bugs: Explain why a deliberate panic can reveal a logic bug that would otherwise go unnoticed, using the three-item vector example.
Why it matters: A deliberate failure can expose a wrong assumption before it goes unnoticed. - Assertion macro conditions: Distinguish the conditions checked by assert!, assert_eq!, and assert_ne!, and describe when their optional messages appear.
Why it matters: Assertions let you state conditions that should hold and get a clear failure when they do not. - Unwrap and expect tradeoffs: Explain why unwrap can be useful while first writing a program, why it is risky in finished code, and what expect improves without preventing a panic.
Why it matters: Clear failure messages help during development, but a finished program should generally avoid crashing for its users. - Fallback with unwrap_or: Describe how unwrap_or supplies a chosen value for None and contrast its non-panicking behavior with a deliberate failure.
Why it matters: A fallback keeps the program running, but it should be a value you actually want when data is absent.
Key terms: panic!, logic bug, Vec, assert!, assert_eq!, assert_ne!, unwrap(), expect, None, unwrap_or()
6.4 Summary
Where this is used later
- Closures: Lazy and fast (chapter 8) · The HashMap collection.
- Blanket trait implementations (chapter 17) · BTreeMap keys and values
- Using a Box to handle multiple error types (chapter 13) · Handling multiple error types extends the section’s single-error-type use of `?`.
- Anyhow (chapter 17) · Using the question mark operator to propagate errors.
- Using files (chapter 18) · The ? operator
- Creating files (chapter 18) · The question mark operator was introduced earlier.
- Using an async run time (chapter 19) · Using the `?` operator for error handling.
- What happens when tests fail (chapter 14) · Provides context for interpreting failures in tests.
Concepts explained in this chapter
- question mark operatorThe question mark operator is Rust syntax that propagates an error from a Result or absence from an Option.
- MapA map is an associative data structure that stores key-value pairs and supports retrieving values by key.
- BTreeMapBTreeMap is Rust's ordered map collection that associates keys with values and iterates them in key order.
- HTTP GETHTTP GET is a request method for retrieving a resource or representation from a server.
- keyA key is a value used to identify and look up an associated value in a map collection.
- ParseIntErrorParseIntError is the Rust standard-library error type returned when text cannot be parsed as an integer.
- panicA panic is Rust's unrecoverable failure behavior that stops ordinary execution and reports diagnostic information.
- assert_eqassert_eq! is a Rust macro that panics when two expressions evaluate to unequal values and reports both values.
- assert_neassert_ne! is a Rust macro that panics when two values are equal.
- assertionA software assertion is an executable check of an expected condition or property that signals failure when the check does not hold.
- expectexpect is a Rust method that extracts the value from an Option or Result and panics with a supplied message when the value represents absence or failure.
- runtime logic errorA logic bug is a defect in a program's intended behavior that does not necessarily prevent the code from running.
- unwrap_orunwrap_or is a Rust method that returns an Option or Result value when present or successful and otherwise returns a specified fallback value without panicking.
- unwrapunwrap is a Rust method that extracts a contained success or present value and panics when the value represents failure or absence.
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.
