Skip to content

Chapter 5 of 8

Slices, Strings, and Deref Coercion

A slice is a reference to a contiguous sequence of elements, written &[T] for a shared slice or &mut [T] for a mutable slice. Slices always borrow data and consist of a pointer plus a length, without owning the underlying storage. They are constructed with range syntax: [..] takes the whole sequence, [a..] from index a through the end, [..b] from the start to b exclusive, and [a..b] the elements from a to b exclusive. Because of unsized coercion, a function accepting &[T] can be called with a &Vec<T>, an array reference, or any other borrowing form that points to a contiguous run of Ts.

The relationship between String and &str is one of the most common applications of these ideas. A String is an owned, growable, heap-allocated UTF-8 buffer, while a &str is an immutable borrowed view into UTF-8 data — there is no way to mutate through a &str, so growing or modifying the text requires a String (or a &mut str). Creating a String from a literal can be done with String::from("hello") or "hello".to_string(), and extracting a borrowed view is as simple as &s (via deref coercion), &s[..], or the explicit s.as_str().

The reason s.len() works whether s is a String or a &str lies in the interaction between Deref and method resolution. The Deref trait, with its Deref::deref method, allows the compiler to dereference through a value with * and to coerce references automatically — a &String coerces to a &str because String implements Deref<Target = str>. This is called deref coercion, and it is what makes function arguments flexible: passing a &Vec<T> where a &[T] is expected works seamlessly. Auto-deref takes this further during method lookup, repeatedly dereferencing the receiver until a method is found, so chained methods on smart pointers often Just Work. The same principle applies to Box<T>, a smart pointer that owns a heap-allocated T and is dropped (along with the inner value) when it goes out of scope.

All chapters
  1. 1Ownership Rules and Memory
  2. 2Moves, Copy, and Clone
  3. 3Borrowing and the Borrow Checker
  4. 4Lifetimes, Variance, and PhantomData
  5. 5Slices, Strings, and Deref Coercion
  6. 6Interior Mutability and Runtime Enforcement
  7. 7Shared Ownership Across Threads
  8. 8Patterns, Iteration, and Unsized Types

Drill it

Reading is not remembering. These come from the Rust Ownership And Borrowing Explained deck:

Q

What are the three ownership rules in Rust?

Each value in Rust has an owner. There can only be one owner at a time. When the owner goes out of scope, the value is dropped.

Q

What happens to a value when its owner goes out of scope in Rust?

It is dropped — its drop function runs and its memory is freed.

Q

Which standard library function is called when a value goes out of scope?

drop, from the Drop trait, is called automatically at the end of the variable's scope.

Q

What is a "move" in Rust?

A transfer of ownership: assigning a value to another variable or passing it to a function moves the value, invalidating the source binding.