Strings and Text Processing

str

The str type (pronounced "string slice") is a primitive, unsized (!Sized), UTF-8 encoded sequence of bytes, almost always accessed behind a borrowed reference as &str (or &mut str, Box<str>, Arc<str>).

Memory Layout of &str

Because str is a Dynamically Sized Type ([u8] guaranteed to be valid UTF-8), a reference &str is a 16-byte fat pointer on 64-bit platforms consisting of a pointer to the first byte and a byte length:

Stack (`&str`, 16 bytes)             Read-Only Binary Data / Heap / Stack
+----------------+                   +---+---+---+---+---+---+---+---+---+
| ptr      (8 B) +-----------------> | r | u | s | t | - | l | a | n | g |
| len      = 4   |                   +---+---+---+---+---+---+---+---+---+
+----------------+                     └───┬───┘
                                       4-byte slice "rust"

Unlike String, &str has no capacity field because it does not own a growable allocation—it is a fixed-length view into UTF-8 bytes stored somewhere in memory.

Where the Bytes Behind a &str Can Live

A &str can borrow UTF-8 text from any memory region:

  1. Static read-only binary data (&'static str): String literals such as "hello" are baked directly into the compiled executable's read-only data segment and have type &'static str.
  2. A heap-allocated String: Borrowing &my_string or &my_string[0..4] creates a &str pointing into my_string's heap buffer.
  3. A stack buffer: Validating a stack byte array [u8; N] via std::str::from_utf8(&buf) returns a &str pointing into the stack frame.
fn describe_slice(label: &str, text: &str) {
    println!(
        "{label:<14} -> '{text}' (len = {} bytes, ptr = {:p})",
        text.len(),
        text.as_ptr()
    );
}

fn main() {
    // 1. Static literal in binary read-only memory:
    let literal: &'static str = "static_literal";

    // 2. Slice into a heap-allocated `String`:
    let heap_string = String::from("heap_allocated_text");
    let heap_slice: &str = &heap_string[0..4];

    // 3. Slice into a stack byte array validated as UTF-8:
    let stack_bytes: [u8; 5] = *b"stack";
    let stack_slice: &str = std::str::from_utf8(&stack_bytes).unwrap();

    describe_slice("Literal", literal);
    describe_slice("Heap slice", heap_slice);
    describe_slice("Stack slice", stack_slice);
}
Literal        -> 'static_literal' (len = 14 bytes, ptr = 0x104a1c000)
Heap slice     -> 'heap' (len = 4 bytes, ptr = 0x600001714020)
Stack slice    -> 'stack' (len = 5 bytes, ptr = 0x16b3ef4eb)

Immutable Heap String Slices (Box<str> and Arc<str>)

When an owned string will never grow or shrink after creation, converting a String into Box<str> (via string.into_boxed_str()) or Arc<str> drops the excess capacity and shrinks the handle from 24 bytes (ptr, len, capacity) to 16 bytes (ptr, len), saving 8 bytes per string in large symbol tables or AST nodes.