Chapter 10: Lifetimes and interior mutability
The introduction explains why lifetime annotations may be needed and introduces interior mutability as a way to change values safely without a mutable reference.
After this chapter you will be able to
- Lifetimes and reference duration. Explain that lifetimes help the compiler determine when variables can be dropped and how long references last, and that annotations are usually unnecessary but sometimes requested by the compiler.
- Interior mutability basic idea. State that interior mutability allows values to be mutated safely without using a mutable reference (`&mut`).
- Two Forms of String Slices. Distinguish a string literal from a borrowed str: a literal has type &'static str and lasts for the whole program, while a reference to a String can be used as a &str without that static lifetime.
- Returning References to Strings. Explain why a function can return a reference to a string literal but cannot return a reference to data that exists only inside the function.
- Purpose of lifetime annotations. Explain that Rust usually handles lifetimes automatically, but sometimes needs a lifetime annotation to supply extra lifetime information.
- Reference lifetime limit. Explain why a reference must not live longer than the object it comes from.
- References returned from functions. Explain why a function cannot return a reference to a String created inside it, and why returning a string literal as &'static str works instead.
- Meaning of lifetime annotations. Describe what &'static str promises to the compiler and distinguish that promise from giving a value a longer lifetime. State when reference lifetimes usually need to be written.
- Struct reference lifetime requirement. Explain why a City struct with a &str name needs a lifetime: the referenced value could be dropped while City still holds the reference.
- Named lifetime as constraint. Explain what struct City<'a> with name: &'a str requires, and distinguish that requirement from making the referenced value live longer.
- Anonymous lifetime in implementations. Explain what `'_` communicates in an `impl` for a struct containing a reference and why it can replace writing out a named lifetime there.
- Selective mutation inside immutable structs. Explain what interior mutability allows and why it would be useful for PhoneModel: its mostly unchanging fields can remain immutable while its on_sale field can change without making the whole struct mutable.
- Changing values with Cell. Explain how the PhoneModel example uses Cell<bool> and .set() to change on_sale to false without a mutable reference.
- RefCell interior mutability. Explain how RefCell lets code change a contained value without declaring the owner mut, using borrow_mut() to make the change.
- RefCell borrowing checks. Describe the rules for borrow() and borrow_mut(), and distinguish RefCell's runtime borrowing checks from compile-time checks.
- Mutex mutual exclusion. Explain how a Mutex allows a value to be changed without declaring it mut and why it is safe to use across threads.
- Mutex guard release. Describe when a Mutex unlocks and how a separate scope, drop(), or a direct change through .lock() affects how long the lock is held.
- Lock contention and try_lock. Explain why trying to lock an already-locked Mutex can leave a program waiting, and how .try_lock() changes that behavior.
- RwLock read and write access. Explain how RwLock combines thread-safe locking with read and write access, and state which combinations of access are allowed.
- Conflicting access and deadlock. Describe what happens when code requests unavailable write access and how dropping an existing access variable or ending its scope avoids the deadlock.
What it builds on
- More on references (chapter 2) · Introduces references.
- Mutable references (chapter 2) · Introduces mutable references.
- Strings (chapter 2) · Assumes familiarity with String and &str.
- Giving references to functions (chapter 2) · Assumes familiarity with references and functions.
- Traits as bounds (chapter 7) · Provides the comparison with a generic trait bound.
- The stack, the heap, pointers, and references (chapter 2) · Introduces references and the memory they point to.
- Mutability (changing) (chapter 1) · The usual use of mut to change a variable.
- Copy types (chapter 2) · The meaning of Copy types.
Section by section
Introduction
The introduction explains why lifetime annotations may be needed and introduces interior mutability as a way to change values safely without a mutable reference.
- Lifetimes and reference duration: Explain that lifetimes help the compiler determine when variables can be dropped and how long references last, and that annotations are usually unnecessary but sometimes requested by the compiler.
Why it matters: This gives you the basic reason Rust may ask for a lifetime annotation. - Interior mutability basic idea: State that interior mutability allows values to be mutated safely without using a mutable reference (`&mut`).
Why it matters: It names the alternative to `&mut` that the chapter will develop.
Key terms: lifetimes, lifetime annotations, references, interior mutability, &mut
10.1 Types of &str
This section distinguishes string literals from borrowed strings and explains why their lifetimes affect whether a function can return a reference to them.
- Two Forms of String Slices: Distinguish a string literal from a borrowed str: a literal has type &'static str and lasts for the whole program, while a reference to a String can be used as a &str without that static lifetime.
Why it matters: The distinction explains why two values used as &str can have different lifetimes. - Returning References to Strings: Explain why a function can return a reference to a string literal but cannot return a reference to data that exists only inside the function.
Why it matters: It connects a reference’s safety to how long the data it points to remains available.
Key terms: string literals, borrowed str, &'static str, &String, &str, reference, 'static lifetime, string literal
10.2 Lifetime annotations
Introduces lifetime annotations as extra information Rust sometimes needs and explains why references cannot outlive the objects they refer to.
- Purpose of lifetime annotations: Explain that Rust usually handles lifetimes automatically, but sometimes needs a lifetime annotation to supply extra lifetime information.
Why it matters: This explains why lifetime annotations appear even though Rust usually handles lifetimes for you. - Reference lifetime limit: Explain why a reference must not live longer than the object it comes from.
Why it matters: This is the safety reason lifetimes matter for references.
Key terms: lifetime, lifetime annotation, reference
10.2.1 Lifetimes in functions
This section explains why a function cannot return a reference to a local String, when it can return a &'static str, and what lifetime annotations do.
- References returned from functions: Explain why a function cannot return a reference to a String created inside it, and why returning a string literal as &'static str works instead.
Why it matters: The value a reference points to must remain alive for as long as that reference is used. - Meaning of lifetime annotations: Describe what &'static str promises to the compiler and distinguish that promise from giving a value a longer lifetime. State when reference lifetimes usually need to be written.
Why it matters: An annotation tells the compiler what your code must satisfy; it cannot repair a reference to a value that dies too soon.
Key terms: String, &str, 'static, string literal, lifetime annotation, &'static str, T: Display
10.2.2 Lifetime annotations in types
The section shows why a struct that stores a string reference needs a lifetime annotation, and why that annotation restricts accepted references rather than extending their lives.
- Struct reference lifetime requirement: Explain why a City struct with a &str name needs a lifetime: the referenced value could be dropped while City still holds the reference.
Why it matters: This explains the safety problem the annotation addresses. - Static lifetime restriction: Distinguish using &'static str for City’s name from accepting a reference that merely lasts long enough to use City.
Why it matters: It prevents using 'static as a blanket fix when a shorter-lived reference is intended. - Named lifetime as constraint: Explain what struct City<'a> with name: &'a str requires, and distinguish that requirement from making the referenced value live longer.
Why it matters: This is the key to reading a lifetime annotation as a condition on a borrow, not a way to keep data alive.
Key terms: &str, reference, lifetime, 'static, &'static str, string literals, City<'a>, name: &'a str, lifetime annotation
10.2.3 The anonymous lifetime
This section explains the anonymous lifetime in implementations, shows how implementations relate lifetimes on traits and structs, and offers practical ways to work through lifetime errors.
- Anonymous lifetime in implementations: Explain what `'_` communicates in an `impl` for a struct containing a reference and why it can replace writing out a named lifetime there.
Why it matters: It helps you understand what the compiler is asking for when you implement behavior for a struct that holds a reference. - Trait and struct lifetime relationships: Distinguish lifetimes declared on a trait from those declared on a struct, and describe how an `impl` can relate them or leave them separate.
Why it matters: Matching lifetime names can be misleading unless you look at how the implementation declares and uses them. - Practical responses to lifetime errors: Describe the section’s approach to lifetime errors: follow compiler suggestions incrementally or use owned values when borrowing becomes too complicated.
Why it matters: You can make progress with lifetime errors without solving every complex borrowing case at once.
Key terms: anonymous lifetime, <'_>, impl block, trait, struct, impl, elide the lifetimes, owned types, String, &'a str
10.3 Interior mutability
Introduces interior mutability as a way to change selected data inside an otherwise immutable struct, using a phone model as an example.
- Selective mutation inside immutable structs: Explain what interior mutability allows and why it would be useful for PhoneModel: its mostly unchanging fields can remain immutable while its on_sale field can change without making the whole struct mutable.
Why it matters: It lets you keep a struct generally immutable when only a specific piece of its data needs to change.
Key terms: interior mutability, mut, PhoneModel, on_sale, &self, &mut self
10.3.1 Cell
This section shows how Cell lets a PhoneModel change its on_sale value without a mutable reference and explains why Cell is best suited to simple Copy types.
- Changing values with Cell: Explain how the PhoneModel example uses Cell<bool> and .set() to change on_sale to false without a mutable reference.
Why it matters: This is the section’s concrete example of interior mutability. - Copy types and value access: Explain why Cell works best with simple Copy types and what restriction applies to its .get() method.
Why it matters: It explains when Cell’s way of accessing its contents is convenient.
Key terms: Cell<bool>, interior mutability, .set(), Cell, .get(), Copy
10.3.2 RefCell
Introduces RefCell as a way to change a value without declaring its owner mutable, explains its borrowing rules and runtime checks, and notes its limitation with multiple threads.
- RefCell interior mutability: Explain how RefCell lets code change a contained value without declaring the owner mut, using borrow_mut() to make the change.
Why it matters: This is the reason to use RefCell in the examples here. - RefCell borrowing checks: Describe the rules for borrow() and borrow_mut(), and distinguish RefCell's runtime borrowing checks from compile-time checks.
Why it matters: Code that compiles can still fail when it violates RefCell's borrowing rules. - Avoiding RefCell borrow panics: Describe the two approaches the section gives for avoiding a panic when changing a RefCell value.
Why it matters: These approaches help avoid an unexpected runtime panic while changing a RefCell value. - RefCell multiple-thread limitation: Explain why the section says Cell and RefCell cannot be used across multiple threads, and why it introduces Mutex and RwLock next.
Why it matters: It marks the limit of Cell and RefCell before the book turns to types intended for multiple threads.
Key terms: RefCell, interior mutability, borrow_mut(), borrow(), immutable borrows, mutable borrow, run time, panic, try_borrow_mut(), double borrow
10.3.3 Mutex
This section explains how a Mutex permits controlled changes to a value, how its lock is released, and how to avoid waiting indefinitely for a lock.
- Mutex mutual exclusion: Explain how a Mutex allows a value to be changed without declaring it mut and why it is safe to use across threads.
Why it matters: The guard explains both how a thread gets access and why another thread cannot change the value simultaneously. - Mutex guard release: Describe when a Mutex unlocks and how a separate scope, drop(), or a direct change through .lock() affects how long the lock is held.
Why it matters: Releasing the guard determines when the Mutex becomes available for another lock attempt. - Lock contention and try_lock: Explain why trying to lock an already-locked Mutex can leave a program waiting, and how .try_lock() changes that behavior.
Why it matters: Choosing between waiting and trying once helps prevent a lock attempt from holding up the program.
Key terms: Mutex, mutual exclusion, .lock(), MutexGuard, drop(), deadlock, .try_lock(), if let, match
10.3.4 RwLock
Explains how RwLock permits read and write access, why conflicting access can deadlock, and how to avoid waiting forever.
- RwLock read and write access: Explain how RwLock combines thread-safe locking with read and write access, and state which combinations of access are allowed.
Why it matters: This distinction tells you when an RwLock permits access to its value. - Conflicting access and deadlock: Describe what happens when code requests unavailable write access and how dropping an existing access variable or ending its scope avoids the deadlock.
Why it matters: Releasing an access variable before requesting conflicting access prevents the program from getting stuck. - Nonblocking access attempts: Describe how .try_read() and .try_write() offer a way to give up when access is unavailable rather than deadlocking forever.
Why it matters: These methods let code respond when access is unavailable instead of remaining stuck.
Key terms: RwLock, read–write lock, .read(), .write(), deadlock, drop(), .try_read(), .try_write()
10.4 Summary
Where this is used later
- Cow (chapter 11) · The meaning of lifetime annotations for references.
- Const generics (chapter 16) · Lifetime parameters
- Spawning threads (chapter 11) · Introduces lifetime annotations in functions.
- Avoiding lifetime annotations with Rc (chapter 11) · Lifetime annotations in types
- The [src] button (chapter 13) · Cell and its .get() method
- OnceCell: A cell to only write to once (chapter 17) · The earlier introduction to Cell provides a point of comparison for its method names.
- Multiple threads (chapter 11) · Provides the basic reason a Mutex can protect a value used across multiple threads.
- Arc (chapter 12) · The earlier introduction to Mutex.
Concepts explained in this chapter
- lifetimeA variable lifetime is the interval during which a variable binding is valid and accessible.
- lifetime annotationA lifetime annotation is Rust syntax that names relationships among the lifetimes of references and values in a type or function signature.
- static lifetimeThe static lifetime is a Rust lifetime that lasts for the entire duration of the program.
- lockingMutual exclusion is a concurrency property that allows at most one thread to access a protected resource at a time.
- refcellRefCell is a Rust standard-library container that enforces borrowing rules at run time and permits mutation through an immutable access path.
- deadlockA deadlock is a state in which threads or operations wait indefinitely for access held by one another.
- anonymous lifetimeAn anonymous lifetime is Rust's inferred lifetime notation, written as `'_'`, for a reference whose specific lifetime name does not need to be written.
- slicingA slice is a dynamically sized borrowed view of a contiguous sequence of values.
- referenceReferencing is the operation of creating a Rust reference with the & operator.
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.
