Slices

Slices vs. Owned Collections

Slices (&[T], &mut [T], &str) are non-owning views into contiguous memory, whereas owned collections (Vec<T>, String, Box<[T]>, [T; N]) own their elements and are responsible for allocating, resizing, and dropping them.

Architectural Comparison

Property &[T] / &str (Slice Reference) Vec<T> / String (Growable Heap Collection) Box<[T]> / Box<str> (Boxed Slice) [T; N] (Fixed Array)
Ownership Borrowed view (Copy for &[T]) Owns heap buffer Owns heap buffer Owns inline elements
Stack size (64-bit) 16 bytes (ptr, len) 24 bytes (ptr, len, cap) 16 bytes (ptr, len) size_of::<T>() * N
Can append / resize? No (can only narrow the view) Yes (.push(), .extend(), .truncate()) No (fixed length after creation) No (compile-time constant N)
Heap allocation None (points to existing stack/heap/static memory) Yes (when cap > 0) Yes (exact size len, no spare capacity) None (stored inline on stack or in parent struct)
Drop behavior No-op (does not drop elements) Drops all len elements and frees heap buffer Drops all len elements and frees heap buffer Drops all N elements
Stack Handles Pointing to Contiguous Sequence Memory:

  Vec<T> (24B owner)             Box<[T]> (16B owner)           &[T] (16B borrowed view)
  +------------------+           +------------------+           +------------------+
  | ptr              |           | ptr              |           | ptr              |
  | len = 3          |           | len = 3          |           | len = 2          |
  | cap = 4          |           +------------------+           +------------------+
  +--------+---------+                    |                              |
           |                              v                              v
           v                     +-----+-----+-----+            (Borrows any subrange of
  +-----+-----+-----+-----+      |  A  |  B  |  C  |             Vec, Box<[T]>, or [T; N]
  |  A  |  B  |  C  |uninit      +-----+-----+-----+             without allocating!)
  +-----+-----+-----+-----+

Converting Between Slices and Owned Collections

Moving from an owned collection to a slice is a zero-cost O(1) pointer copy, whereas converting a borrowed slice into an owned heap collection performs an O(n) allocation and element clone/copy:

Direction Conversion Expression Cost
Vec<T> / [T; N] -> &[T] &vec, vec.as_slice(), &arr O(1) zero-copy
String -> &str &s, s.as_str() O(1) zero-copy
&[T] -> Vec<T> slice.to_vec() or Vec::from(slice) (requires T: Clone) O(n) heap allocation + clone
&str -> String str_slice.to_owned() or String::from(str_slice) O(n) heap allocation + byte copy
Vec<T> -> Box<[T]> vec.into_boxed_slice() Drops excess capacity (shrink_to_fit) and converts in place
Box<[T]> -> Vec<T> boxed.into_vec() O(1) zero-copy (cap set to len)
&[T] -> [T; N] slice.try_into() (requires T: Copy) O(N) stack copy if slice.len() == N
fn main() {
    // 1. Owned collection -> Slice (O(1), zero allocation)
    let owned_vec = vec![10, 20, 30, 40];
    let view: &[i32] = &owned_vec[1..3];

    // 2. Slice -> Owned Vec<T> (allocates a new heap buffer for `[20, 30]`)
    let cloned_subvec: Vec<i32> = view.to_vec();

    // 3. Slice -> Fixed-size array `[i32; 2]` via `TryFrom`
    let fixed_pair: [i32; 2] = view.try_into().expect("slice has length 2");

    println!("Original Vec:  {owned_vec:?}");
    println!("Slice view:    {view:?}");
    println!("Cloned Vec:    {cloned_subvec:?}");
    println!("Fixed [i32;2]: {fixed_pair:?}");
}
Original Vec:  [10, 20, 30, 40]
Slice view:    [20, 30]
Cloned Vec:    [20, 30]
Fixed [i32;2]: [20, 30]

Shrinking the View vs. Mutating the Collection

Calling .split_first() or reassigning slice = &slice[1..] on a slice reference only advances its local ptr and decrements its len—the underlying Vec<T> or array is untouched:

fn main() {
    let backing = vec!["alpha", "beta", "gamma"];
    let mut window: &[&str] = &backing;

    // Narrow the slice view by 1 element:
    window = &window[1..];

    println!("Narrowed slice window: {window:?}");
    println!("Backing Vec unchanged: {backing:?}");
}
Narrowed slice window: ["beta", "gamma"]
Backing Vec unchanged: ["alpha", "beta", "gamma"]