src/memory-management/review.md
Programs allocate memory in two ways:
Stack: Continuous area of memory for local variables.
Heap: Storage of values outside of function calls.
Creating a String puts fixed-sized metadata on the stack and dynamically sized
data, the actual string, on the heap:
# // Copyright 2023 Google LLC
# // SPDX-License-Identifier: Apache-2.0
#
fn main() {
let s1 = String::from("Hello");
}
Stack
.- - - - - - - - - - - - - -. Heap
: : .- - - - - - - - - - - - - - - -.
: s1 : : :
: +-----------+-------+ : : :
: | capacity | 5 | : : +----+----+----+----+----+ :
: | ptr | o-+---+-----+-->| H | e | l | l | o | :
: | len | 5 | : : +----+----+----+----+----+ :
: +-----------+-------+ : : :
: : : :
`- - - - - - - - - - - - - -' `- - - - - - - - - - - - - - - -'
Mention that a String is backed by a Vec, so it has a capacity and length
and can grow if mutable via reallocation on the heap.
If students ask about it, you can mention that the underlying memory is heap allocated using the System Allocator and custom allocators can be implemented using the Allocator API
We can inspect the memory layout with unsafe Rust. However, you should point
out that this is rightfully unsafe!
# // Copyright 2023 Google LLC
# // SPDX-License-Identifier: Apache-2.0
#
fn main() {
let mut s1 = String::from("Hello");
s1.push(' ');
s1.push_str("world");
// DON'T DO THIS AT HOME! For educational purposes only.
// String provides no guarantees about its layout, so this could lead to
// undefined behavior.
unsafe {
let (capacity, ptr, len): (usize, usize, usize) = std::mem::transmute(s1);
println!("capacity = {capacity}, ptr = {ptr:#x}, len = {len}");
}
}