Slices

Array Slices

An array slice (&[T] or &mut [T]) is a dynamically sized view into a contiguous sequence of elements of type T inside an array ([T; N]), vector (Vec<T>), or another slice. While an array type [T; N] bakes its compile-time length N into the type itself, an array slice &[T] erases the compile-time length N and carries its length at runtime inside a 16-byte fat pointer (ptr + len).

Fixed-Size Array ([T; N]) vs Array Slice (&[T])

Property Fixed-Size Array [T; N] (or &[T; N]) Array Slice &[T] / &mut [T]
Owns the elements? [T; N] owns elements; &[T; N] borrows all N elements Borrows a contiguous range of elements
Length known at Compile time (N is part of the type) Runtime (slice.len() stored in fat pointer)
Pointer size (64-bit) &[T; N] is 8 bytes (thin pointer—length is known from N) &[T] is 16 bytes (8-byte ptr + 8-byte len)
Can point to a sub-range? No (always refers to the entire N-element array) Yes (&arr[start..end] can view any contiguous sub-range)
Accepts arrays of different lengths? No (&[i32; 3] rejects &[i32; 5]) Yes (accepts &[i32; 3], &[i32; 5], and &Vec<i32>)
Stack Array (`arr: [i32; 5]`):
  +-----+-----+-----+-----+-----+
  | 100 | 200 | 300 | 400 | 500 |
  +-----+-----+-----+-----+-----+
    [0]   [1]   [2]   [3]   [4]
          ^           ^
          │           │
  `let sub: &[i32] = &arr[1..=3];`  -->  `ptr` points to `arr[1]`, `len = 3` (`[200, 300, 400]`)

Creating Array Slices via Unsized Coercion and Range Indexing

There are two ways to obtain an array slice &[T] or &mut [T] from an array:

  1. Whole-array unsized coercion (&arr or &mut arr): When you assign &arr to a variable or parameter of type &[T] (or call arr.as_slice() / arr.as_mut_slice()), Rust automatically converts the thin array reference &[T; N] into a fat slice reference &[T] with len = N.
  2. Range indexing (&arr[range] or &mut arr[range]): Indexing an array with any range operator (start..end, start..=end, ..end, start.., ..) produces a slice covering that sub-range.
fn print_slice(label: &str, slice: &[i32]) {
    println!("{label:<16} (len {}): {slice:?}", slice.len());
}

fn main() {
    let numbers: [i32; 6] = [10, 20, 30, 40, 50, 60];

    // 1. Implicit unsized coercion from `&[i32; 6]` to `&[i32]`:
    print_slice("Full (&numbers)", &numbers);

    // 2. Range slicing into sub-arrays:
    print_slice("&numbers[1..4]", &numbers[1..4]);
    print_slice("&numbers[..3]", &numbers[..3]);
    print_slice("&numbers[4..]", &numbers[4..]);
    print_slice("Empty [2..2]", &numbers[2..2]);
}
Full (&numbers)  (len 6): [10, 20, 30, 40, 50, 60]
&numbers[1..4]   (len 3): [20, 30, 40]
&numbers[..3]    (len 3): [10, 20, 30]
&numbers[4..]    (len 2): [50, 60]
Empty [2..2]     (len 0): []

Splitting and Chunking Array Slices

Because creating sub-slices only computes a new (ptr, len) pair without copying elements, standard slice methods let you partition an array into non-overlapping or overlapping sub-slices in $O(1)$ time per step:

fn main() {
    let mut data: [i32; 6] = [1, 2, 3, 4, 5, 6];

    // `split_at_mut(mid)` safely splits one `&mut [i32]` into TWO disjoint mutable slices!
    let (left, right) = data.split_at_mut(3);
    left[0] = 100;
    right[0] = 400;
    println!("After split_at_mut(3): {data:?}");

    // Iterate over fixed-size non-overlapping chunks of 2 elements:
    for (idx, pair) in data.chunks(2).enumerate() {
        println!("  Chunk {idx}: {pair:?}");
    }
}
After split_at_mut(3): [100, 2, 3, 400, 5, 6]
  Chunk 0: [100, 2]
  Chunk 1: [3, 400]
  Chunk 2: [5, 6]

Converting an Array Slice Back to a Fixed-Size Array (TryFrom)

When you have a slice &[T] whose length is only known at runtime, and you need a fixed-size array [T; N] (or &[T; N]), use TryFrom / .try_into(). The conversion checks slice.len() == N at runtime and returns Ok([T; N]) if the length matches, or Err(TryFromSliceError) if not:

fn parse_ipv4_header(packet: &[u8]) -> Option<[u8; 4]> {
    // Extract the first 4 bytes as `&[u8]` and convert to `[u8; 4]`:
    let first_four: &[u8] = packet.get(0..4)?;
    first_four.try_into().ok()
}

fn main() {
    let raw_bytes: &[u8] = &[192, 168, 1, 1, 80, 90];
    let short_bytes: &[u8] = &[127, 0];

    println!("Valid 4-byte array: {:?}", parse_ipv4_header(raw_bytes));
    println!("Too short slice:    {:?}", parse_ipv4_header(short_bytes));
}
Valid 4-byte array: Some([192, 168, 1, 1])
Too short slice:    None