Chapter 6: Memory
Introduces memory as a shared resource and explains that the OS gives programs a simplified view of its availability and location.
After this chapter you will be able to
- OS-mediated view of memory. Explain why a program’s view of memory does not necessarily reflect how much memory is available or where it is located.
- Pointers and address spaces. Explain that pointers identify data elsewhere by using memory addresses, and describe an address space as the orderly range of bytes a program can address rather than a direct picture of its physical RAM.
- Address pointer reference distinctions. Distinguish a memory address, a pointer, and a Rust reference by what each identifies and what information or guarantees it adds.
- Rust reference guarantees. Describe the validity, legal-access, alignment, and dynamically sized data guarantees that the section attributes to Rust references.
- Reference and pointer terminology. Distinguish how this section uses references, pointers, and raw pointers, especially who is responsible for safety.
- References in shared address space. Describe what the references in listing 6.1 hold and why arrows to disconnected arrays are an incomplete picture of memory.
- Raw pointer types and addresses. Describe `*const T` and `*mut T` as raw pointer types and distinguish a typed pointer from a memory address. Explain that a pointer identifies the starting byte of a value of type `T`, whose width determines how many bytes are fetched.
- Unsafe raw pointer access. Explain why creating a raw pointer is permitted but accessing its value requires an `unsafe` block. Describe the validity and shared-access guarantees that raw pointers lack.
- Smart pointer added semantics. Explain how Rust’s smart pointers provide a safer alternative to raw pointers by adding semantics beyond dereferencing a memory address.
- Interior mutability tradeoff. Explain why interior mutability is useful when an interface takes immutable values, and describe the wrappers and runtime cost mentioned here.
- Shared ownership tradeoff. Explain when shared ownership can simplify access to a resource and state its runtime tradeoff.
- Extensible pointer foundations. Explain that Rust’s programmer-facing pointer types are built from more primitive types, and identify Unique and Shared as foundations for different pointer types.
- Stack and heap tradeoff. Explain the section’s rule of thumb that the stack is fast, the heap is slow, and that this motivates preferring the stack when in doubt.
- Compile-time size requirement. Explain why the section translates its stack preference into advice to use Rust types that implement Sized.
- Stack frames and call order. Explain what a stack frame holds and how frames and the stack pointer change as functions are called and return.
- Read-only string argument flexibility. Describe how an AsRef<str> generic parameter lets a read-only function accept both String and &str arguments.
- Heap and variable size. Explain the two meanings of a type not having a known size at compile time, using the section’s examples.
- Pointer access to heap values. Describe how a boxed heap value relates to a pointer on the stack and how the example accesses that value.
- Dynamic allocation process. Describe why a running program requests more memory and the three steps of dynamic memory allocation: request memory, use it, and release it when no longer needed. Explain that an allocator sits between the program and the OS and can reduce work.
- Stack and heap trade-off. Explain the section’s reasons for faster stack access and the flexibility of heap-allocated structures. Distinguish these conceptual abstractions from physical partitions of RAM.
- Heap allocation timing. Describe what the allocation report measures and what the plotted results show about allocation size and duration.
- Memory Access and CPU Speed. Explain why supplying memory efficiently can help a CPU compute faster.
- Virtual and real memory. Distinguish a running program’s view of memory from the operating system’s view of physical memory, and describe the roles of the page table and MMU in address translation.
- Page faults and illegal addresses. Distinguish a page fault from a segmentation fault and explain how a page fault relates to swapping.
- Invalid Memory Accesses. Explain why scanning memory from address 0 crashes and why starting at address 1 does not fix the scan. Describe what a segmentation fault indicates in this example.
- Scattered Virtual Addresses. Describe how the addresses of existing variables challenge the idea that a program’s memory runs continuously from zero to roughly its RAM usage. Relate the observed wide range of addresses to virtual addresses and pockets of valid values.
- Virtual address translation. Explain that a program accesses virtual addresses, while the CPU translates them to physical addresses using mapping instructions stored by the OS.
- Pages and translation cache. Describe why virtual memory is divided into pages and how the CPU’s translation lookaside buffer reduces the cost of repeated address translation.
- Overallocation behind virtual memory. Explain how an OS can accommodate requests exceeding physical RAM while preserving a program’s view of its memory.
- OS-assisted address-space inspection. Explain why inspecting a running program’s address space involves the OS, and describe how the section’s Windows example does so.
- Process memory access APIs. Explain that operating systems provide ways to read and write memory, including memory belonging to another program, and identify the analogous Windows and Linux APIs named here.
What it builds on
- Using references (chapter 2) · The reader has previously used Rust references.
- What is an owner? Does it have any responsibilities? (chapter 4) · Assumes the distinction between owning a value and referring to one.
- Wrap data within specialty types (chapter 4) · The idea of wrapping data in specialty types to address ownership constraints.
- Defining functions (chapter 2) · Assumes familiarity with calling functions and passing arguments.
- Generic functions (chapter 2) · Assumes familiarity with generic function parameters.
- Vectors (chapter 2) · Assumes familiarity with owned, growable values such as String.
- Slices (chapter 2) · Introduces slices.
Section by section
Introduction
Introduces memory as a shared resource and explains that the OS gives programs a simplified view of its availability and location.
- OS-mediated view of memory: Explain why a program’s view of memory does not necessarily reflect how much memory is available or where it is located.
Why it matters: Recognizing that a program sees an OS-mediated view of memory sets up the chapter’s later discussion of virtual memory.
Key terms: memory, OS, shared resource, arbiter
6.1 Pointers
This section explains how pointers identify memory, distinguishes addresses, pointers, and references, and introduces the guarantees Rust references provide.
- Pointers and address spaces: Explain that pointers identify data elsewhere by using memory addresses, and describe an address space as the orderly range of bytes a program can address rather than a direct picture of its physical RAM.
Why it matters: This gives you a concrete way to think about what a pointer identifies without mistaking the program’s address space for physical RAM. - Address pointer reference distinctions: Distinguish a memory address, a pointer, and a Rust reference by what each identifies and what information or guarantees it adds.
Why it matters: These distinctions make it easier to follow why Rust treats references differently from raw pointers. - Rust reference guarantees: Describe the validity, legal-access, alignment, and dynamically sized data guarantees that the section attributes to Rust references.
Why it matters: These guarantees explain why a Rust reference offers more than a typed memory location. - Pointer diagram notation: Describe what the arrow and memory blocks represent in the section’s pointer diagram.
Why it matters: Reading the diagram correctly helps you follow the chapter’s later memory illustrations.
Key terms: pointer, memory address, usize, address space, raw pointer, reference, dynamically sized types, referent, aligned, length
6.2 Exploring Rust’s reference and pointer types
Introduces the book’s distinction between references and pointers, uses a small example to relate references to addresses in a shared address space, and notes a performance use for Cow.
- Reference and pointer terminology: Distinguish how this section uses references, pointers, and raw pointers, especially who is responsible for safety.
Why it matters: This vocabulary clarifies what safety guarantees the book means when it discusses pointer types. - References in shared address space: Describe what the references in listing 6.1 hold and why arrows to disconnected arrays are an incomplete picture of memory.
Why it matters: Thinking in terms of addresses in one shared space makes the diagrams more useful without mistaking their layout for separate memories. - Cow and buffer copying: Explain why Cow can be useful when a buffer comes from an external source.
Why it matters: It connects one named smart pointer to the practical cost of copying an external buffer.
Key terms: references, pointers, raw pointers, safety guarantees, addresses, address space, B, C, Cow, copy on write
6.2.1 Raw pointers in Rust
This section explains what Rust raw pointers represent, why dereferencing them is unsafe, and when their use may be justified.
- Raw pointer types and addresses: Describe `*const T` and `*mut T` as raw pointer types and distinguish a typed pointer from a memory address. Explain that a pointer identifies the starting byte of a value of type `T`, whose width determines how many bytes are fetched.
Why it matters: This distinction connects the address a program prints with the value it can access through a pointer. - Unsafe raw pointer access: Explain why creating a raw pointer is permitted but accessing its value requires an `unsafe` block. Describe the validity and shared-access guarantees that raw pointers lack.
Why it matters: An `unsafe` block marks access whose safety the programmer, rather than the compiler, must ensure. - References and exceptional pointer use: Explain why references should normally be preferred even though they are implemented as raw pointers. State the limited circumstances the section gives for using raw pointers instead.
Why it matters: This gives a practical default—use references—and a reason to recognize exceptional raw-pointer code.
Key terms: raw pointer, *const T, *mut T, memory address, dereferencing, unsafe block, referent, references, raw pointers, extra guarantees
6.2.2 Rust’s pointer ecosystem
This section introduces Rust’s range of pointer types and explains interior mutability and shared ownership as useful capabilities with runtime costs.
- Smart pointer added semantics: Explain how Rust’s smart pointers provide a safer alternative to raw pointers by adding semantics beyond dereferencing a memory address.
Why it matters: It gives you a way to think about why Rust offers multiple pointer-like types rather than relying only on raw pointers. - Thin and fat pointer widths: Distinguish thin and fat pointers by the memory-layout widths given in the section.
Why it matters: These terms describe a pointer’s memory layout, not whether it has extra capabilities. - Interior mutability tradeoff: Explain why interior mutability is useful when an interface takes immutable values, and describe the wrappers and runtime cost mentioned here.
Why it matters: It explains why code might combine pointer-like wrappers when ordinary immutable access is too restrictive. - Shared ownership tradeoff: Explain when shared ownership can simplify access to a resource and state its runtime tradeoff.
Why it matters: It identifies when paying for shared access may make a program easier to structure.
Key terms: raw pointers, smart pointer, wrapper type, fat pointer, thin pointer, usize, interior mutability, Cell<T>, RefCell<T>, Rc<T>
6.2.3 Smart pointer building blocks
The section identifies Rust’s extensible smart-pointer building blocks and briefly explains what several of them are used for.
- Extensible pointer foundations: Explain that Rust’s programmer-facing pointer types are built from more primitive types, and identify Unique and Shared as foundations for different pointer types.
Why it matters: These foundations show where to start investigating a smart pointer with different semantics. - Weak references and cycles: Explain why Weak can help with deeply interlinked data structures and distinguish the single-threaded and multi-threaded variants named here.
Why it matters: Weak is an option to investigate when links between values might otherwise form persistent cycles. - Allocation and mutability building blocks: Describe the roles the section assigns to RawVec and UnsafeCell as starting points for investigating custom pointer types.
Why it matters: These examples point to different lower-level components depending on whether allocation or interior mutability is of interest.
Key terms: smart pointer, core::ptr::Unique, core::ptr::Shared, Box<T>, Rc<T>, Arc<T>, std::rc::Weak, std::arc::Weak, Rc, Arc
6.3 Providing programs with memory for their data
Introduces the stack–heap performance tradeoff and the compile-time size requirement behind the advice to prefer stack-friendly types in Rust.
- Stack and heap tradeoff: Explain the section’s rule of thumb that the stack is fast, the heap is slow, and that this motivates preferring the stack when in doubt.
Why it matters: This rule of thumb frames the later discussion of memory allocation and performance. - Compile-time size requirement: Explain why the section translates its stack preference into advice to use Rust types that implement Sized.
Why it matters: It connects the memory-placement rule of thumb to a concrete property of Rust types.
Key terms: stack, heap, Sized, compile time
6.3.1 The stack
This section explains how stack frames support function calls and local variables, then shows how Rust functions can accept both string slices and owned strings.
- Stack frames and call order: Explain what a stack frame holds and how frames and the stack pointer change as functions are called and return.
Why it matters: It connects function calls and returns to the memory used for each call. - Stack analogy and local access: Distinguish the computing stack from a literal stack of plates, and explain the stated reason it provides fast access to local variables.
Why it matters: The analogy is useful for call order, but taking it literally gives the wrong picture of stored data. - Read-only string argument flexibility: Describe how an AsRef<str> generic parameter lets a read-only function accept both String and &str arguments.
Why it matters: A library function can be easier to call without restricting callers to one string type. - String conversion and reuse cost: Explain the conversion approach given when a function needs a String rather than read-only access, and its efficiency risk.
Why it matters: Flexible arguments are useful, but repeated conversions can create avoidable work.
Key terms: stack frame, stack pointer, Last In, First Out (LIFO), call site, stack, stack frames, local variables, &str, String, AsRef<str>
6.3.2 The heap
Introduces the heap as an area of program memory, explains why some values need it, and shows how Box<T> provides pointer-based access to heap values.
- Heap and variable size: Explain the two meanings of a type not having a known size at compile time, using the section’s examples.
Why it matters: This distinction explains why compile-time size is a concern without treating every type of uncertain size as a growing value. - Pointer access to heap values: Describe how a boxed heap value relates to a pointer on the stack and how the example accesses that value.
Why it matters: It lets you explain what a boxed variable holds and why the example uses * to get at the integer. - Early removal with drop: Describe what std::mem::drop does in the heap example and why it is used instead of directly calling a value’s drop() method.
Why it matters: It explains the deliberate removal of one value in the example’s changing memory layout.
Key terms: heap, String, Vec<T>, dynamically sized types, slices, Box<T>, Box::new(T), pointer, stack, dereference operator
6.3.3 What is dynamic memory allocation?
The section explains how programs obtain and release memory dynamically, why stack and heap data differ in performance and flexibility, and how a toy program begins to measure allocation costs.
- Dynamic allocation process: Describe why a running program requests more memory and the three steps of dynamic memory allocation: request memory, use it, and release it when no longer needed. Explain that an allocator sits between the program and the OS and can reduce work.
Why it matters: This gives you the basic sequence behind obtaining memory while a program runs. - Stack and heap trade-off: Explain the section’s reasons for faster stack access and the flexibility of heap-allocated structures. Distinguish these conceptual abstractions from physical partitions of RAM.
Why it matters: The performance difference is a trade-off in layout and access, not a division of physical memory into two parts. - Allocation measurement setup: Describe why the toy program creates and destroys many values and what its ReportingAllocator records.
Why it matters: Repeated measurements provide a way to investigate allocation cost rather than merely assume its size. - Inline closure basics: Describe what the vertical bars and braces mean in the shown closure and what a closure can access.
Why it matters: This is enough syntax to recognize the closure used in the allocation example.
Key terms: dynamic memory allocation, allocator, system call, stack, heap, contiguous layout, cache-friendly, pointer, ReportingAllocator, dynamic memory allocations
6.3.4 Analyzing the impact of dynamic memory allocation
The section uses allocation timing data to show why heap allocation cost can be unpredictable, then considers when that cost matters and how to reduce allocations.
- Heap allocation timing: Describe what the allocation report measures and what the plotted results show about allocation size and duration.
Why it matters: Allocation size alone is a poor predictor of how long a request will take. - Potential cost of allocation delays: Explain why allocation delays might matter to an application despite usually being unimportant.
Why it matters: Even a short delay can matter when it repeatedly interrupts fast work. - Strategies for fewer heap allocations: Describe the two general strategies given for minimizing heap allocations and the caution attached to precreating objects.
Why it matters: These are possible ways to reduce allocation overhead, but one trades away a Rust safety check.
Key terms: heap allocation, allocation size, nanoseconds, allocation, delay, arrays of uninitialized objects, allocator, arena::Arena, arena::TypedArena, lifetime checks
6.4 Virtual memory
Introduces virtual memory by explaining why efficient memory access matters to CPU performance, without yet defining virtual memory.
- Memory Access and CPU Speed: Explain why supplying memory efficiently can help a CPU compute faster.
Why it matters: It gives a reason to care about how programs access memory.
Key terms: CPUs, memory, computer architecture
6.4.1 Background
Defines the basic terms needed to discuss a running program’s virtual memory and how its addresses relate to physical memory.
- Virtual and real memory: Distinguish a running program’s view of memory from the operating system’s view of physical memory, and describe the roles of the page table and MMU in address translation.
Why it matters: This distinction gives you the vocabulary to explain why a program’s addresses are not simply physical-memory locations. - Pages words and segments: Describe the units called pages, words, and segments as defined in this section.
Why it matters: These terms identify the blocks and sizes used when describing memory and its translation. - Page faults and illegal addresses: Distinguish a page fault from a segmentation fault and explain how a page fault relates to swapping.
Why it matters: The difference is whether the requested address is valid but absent from RAM, or illegal.
Key terms: virtual memory, real memory, address space, page table, MMU, page, word, segment, usize, isize
6.4.2 Step 1: Having a process scan its own memory
The section tests the idea that a process’s memory is one continuous range, using failed scans and variable addresses to show that some addresses are inaccessible while valid values occupy scattered pockets of virtual address space.
- Invalid Memory Accesses: Explain why scanning memory from address 0 crashes and why starting at address 1 does not fix the scan. Describe what a segmentation fault indicates in this example.
Why it matters: A nonzero address is not necessarily an address your program can read. - Scattered Virtual Addresses: Describe how the addresses of existing variables challenge the idea that a program’s memory runs continuously from zero to roughly its RAM usage. Relate the observed wide range of addresses to virtual addresses and pockets of valid values.
Why it matters: A program’s RAM usage does not tell you the range of addresses its variables may have.
Key terms: NULL pointer, raw pointer, unsafe block, segmentation fault, virtual addresses, address space, stack, heap
6.4.3 Translating virtual addresses to physical addresses
This section explains how virtual addresses map to physical memory, what page-based mapping enables, and why memory layout affects performance.
- Virtual address translation: Explain that a program accesses virtual addresses, while the CPU translates them to physical addresses using mapping instructions stored by the OS.
Why it matters: This distinction explains why the addresses a program sees need not correspond directly to locations in RAM. - Pages and translation cache: Describe why virtual memory is divided into pages and how the CPU’s translation lookaside buffer reduces the cost of repeated address translation.
Why it matters: Pages and cached translations make virtual addressing practical without requiring an individual mapping for every variable. - Memory locality and performance: Apply the section’s guidance on keeping frequently used data within few pages, avoiding deeply nested pointers, and checking array traversal order.
Why it matters: These are practical ways to make data access friendlier to the translation cache and hardware memory reads. - Overallocation behind virtual memory: Explain how an OS can accommodate requests exceeding physical RAM while preserving a program’s view of its memory.
Why it matters: A program’s apparent available memory can differ from the RAM currently holding its data. - Shared pages and protection: Describe how page mappings can let programs share physical data and how page attributes restrict access.
Why it matters: Page mappings can save memory while page attributes help enforce boundaries between programs. - Executable address space creation: Trace the representative path from an executable file to a program beginning execution in its virtual address space.
Why it matters: It connects the bytes in an executable file to the address space in which its instructions run.
Key terms: virtual addresses, physical addresses, memory management unit (MMU), pages, translation lookaside buffer, TLB, hot working portions, cache line, row-wise, column-wise
6.4.4 Step 2: Working with the OS to scan an address space
The section explains that a running program can inspect its address space by requesting information from the OS through the Windows API.
- OS-assisted address-space inspection: Explain why inspecting a running program’s address space involves the OS, and describe how the section’s Windows example does so.
Why it matters: It connects the OS’s role in memory mapping to a practical way to explore a program’s address space.
Key terms: address space, virtual address, physical address, system call, Windows API, KERNEL.DLL
6.4.5 Step 3: Reading from and writing to process memory
This section explains that operating systems let programs read and write process memory and names the corresponding Windows and Linux APIs.
- Process memory access APIs: Explain that operating systems provide ways to read and write memory, including memory belonging to another program, and identify the analogous Windows and Linux APIs named here.
Why it matters: These APIs explain how tools such as debuggers can inspect or change a program’s memory.
Key terms: process memory, Just-In-Time compilers (JITs), process_vm_readv(), process_vm_writev(), ReadProcessMemory(), WriteProcessMemory()
6.5 Summary
Where this is used later
- Writing to the screen with VGA-compatible text mode (chapter 11) · The use of raw pointers to address memory.
- Introduction to closures (chapter 10) · Develops closures beyond this brief introduction.
- WebAssembly (chapter 10) · The distinction between a process’s address space and virtual memory.
- Why use an operating system (OS) at all? (chapter 10) · The concept of virtual memory
Concepts explained in this chapter
- address spaceAn address space is the set of memory addresses available for identifying stored data and instructions in an execution environment.
- referenceA reference is a non-owning value that provides access to another value without transferring its ownership.
- memory locationA memory address is a numeric location identifying where data or instructions reside in computer memory.
- dynamically sized typeA dynamically sized type is a type whose size is not known at compile time and whose values require runtime size information.
- smart pointerA smart pointer is a pointer-like data structure that provides additional capabilities such as ownership management, reference counting, or synchronization.
- heapThe heap is a region of memory used for dynamically allocated data whose lifetime and size are not determined solely by the call stack.
- virtual addressA virtual address is an address in a process's virtual address space that is translated by the operating system and hardware to physical memory.
- MMUA memory management unit is processor hardware that translates virtual addresses into physical addresses and enforces memory-access attributes.
- memory pageA page is a fixed-size block of virtual memory that is mapped as a unit to physical memory or secondary storage.
- swappingSwapping is the movement of inactive memory contents between RAM and secondary storage to manage limited physical memory.
- translation lookaside bufferA translation lookaside buffer is a processor cache of recently used virtual-to-physical address translations.
- pointer-sized integerisize is Rust's signed integer type whose width matches the target platform's pointer size.
- segmentA segment is a subdivision of a process memory region with associated placement or access boundaries.
- segmentation faultA segmentation fault is an execution failure caused by attempting to access a memory region without permission or outside its valid bounds.
- memory allocationMemory allocation is the process of reserving memory for data or program execution.
- memory allocatorAn allocator is a program component that manages dynamic memory requests and may optimize interaction with the operating system.
- BoxBox is Rust's owning smart pointer type for storing a value on the heap while keeping a pointer to it in the containing value.
- AsRefAsRef is a Rust standard-library trait for cheaply converting a value into a shared reference to a specified target type.
- slicingA slice is a dynamically sized, fixed-length view of contiguous elements that does not own or resize the underlying storage.
- stack frameA stack frame is a per-function-call memory record containing call state, arguments, local variables, and return information.
- stack pointerA stack pointer is a CPU-maintained pointer to the current location or top of a call stack.
- stringString is Rust's owned, growable UTF-8 string type whose character data is stored in heap-allocated memory.
- cache lineA cache line is a small fixed-size block of data transferred between RAM and a processor cache as a unit.
- memory pagingPaging is a memory-management technique that divides virtual memory into fixed-size pages and moves or maps those pages independently.
- closureA closure is an anonymous callable value that can contain an expression body and capture values from its surrounding scope.
- dereferencingDereferencing is the operation of accessing the value designated by a pointer or reference.
- unsafe blockAn unsafe block is a Rust code region in which operations requiring explicit programmer responsibility for safety are permitted.
- execution context stackA call stack is the runtime structure that records active function calls, their local data, and the points to which control returns.
- page faultA page fault is a memory-access condition in which the requested address is unavailable and the required data must be fetched into memory.
- interior mutabilityInterior mutability is a design property allowing a value's internal state to change through an otherwise immutable interface.
- memoryRAM is volatile computer memory that stores data and instructions available to a running processor.
- workflowA process is a running program instance with its own execution context and operating-system-managed resources, including an address space.
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.
