Slices

Slice Basics

Rust by example - slices

A slice is a view to a contiguous sequence of elements within a collection (array, vector, etc.).

The slice type is [T], where T represents the element type (e.g. u8). Because it is dynamically sized, it is most often found behind a pointer:

  • Immutable slice reference: &[T],
  • Mutable slice reference: &mut [T],
  • Boxed slice (on the heap): Box<[T]>,
  • Shareable reference-counted slice: Rc<[T]> or Arc<[T]>.

A few important points:

  • Slices do not own the data they point to. This makes them efficient, as they avoid unnecessary copying.
  • They allow you to work with parts of a collection, enabling flexibility in data manipulation.
  • Slice references are "fat pointers", i.e. they store a pointer and the length of the sequence they refer to, thus they have twice the size of pointers to regular Sized types.
  • As a primitive type, slice implements a large number of methods, for example to swap two elements; reverse the order of elements in the slice in place; iterate elements; create chunks or overlapping windows into the slice; figure out if the slice contains an element with the given value, etc. See also:

Empty slices can be created:

let empty = &x[..0]; // Same as `&x[0..0]`.
assert_eq!(empty, &[]);

Common Use Cases

  • Accessing Subsets of Data: Slices are frequently used to access specific portions of arrays, vectors, or strings without copying the entire collection.
  • Function Arguments: Slices are often used as function arguments when you want to operate on a part of a collection.
  • String Manipulation: string slices (&str) are a common way to reference a string literal or a portion of a String (see below).

Create Slices from Arrays or Vectors

Slices can be created by referencing a portion of a collection using a range. The range can be specified using [start..end], where start is the index of the first element to include, and end is the index of the element after the last one to include. If start is omitted, the slice starts from the beginning of the collection. If end is omitted, the slice extends to the end of the collection. [..] refers to the entire collection.

//! A slice is a view into a contiguous sequence of elements in a collection.
//! It does not own the data it points to.

/// Create slices from an array:
fn slice_from_array() {
    let array: [i32; 5] = [1, 2, 3, 4, 5];

    // Create a slice referencing elements at indices 1, 2, and 3:
    let slice: &[i32] = &array[1..4];
    println!("{slice:?}"); // Output: [2, 3, 4].

    // `[..]` refers to the entire collection.
    let all: &[i32] = &array[..];
    println!("Entire array: {all:?}");

    // You can also coerce an array to a slice:
    let arr_slice: &[i32] = &[10, 20];
    println!("{arr_slice:?}");
}

/// Create slices from a `Vec`:
fn slice_from_vector() {
    let mut vector: Vec<i32> = vec![1, 2, 3, 4, 5];

    let slice: &[i32] = &vector[1..4]; // [2, 3, 4].
    println!("{slice:?}");

    // Mutable slice:
    let mutable_slice: &mut [i32] = &mut vector[2..];
    mutable_slice[0] = 10; // Modifies the original vector.
    println!("{mutable_slice:?}"); // Output: [1, 2, 10, 4, 5].
}

fn main() {
    slice_from_array();
    slice_from_vector();
}

Use a Slice as a Function Argument

You will commonly see slices as function arguments, because Vec<T> implements Deref<Target = [T]>. Therefore, you can simply pass a &Vec<T> to a function that accepts &[T]:

//! Example of a slice as a function argument.

fn sum(slice: &[i32]) -> i32 {
    let mut sum = 0;
    // Note: immutable (and mutable) slices implement `IntoIterator`.
    // The iterator yields references to the slice elements.
    for &element in slice {
        sum += element;
    }
    sum
}

fn main() {
    let vector: Vec<i32> = vec![1, 2, 3, 4, 5];

    // `Vec<T>` implements `Deref<Target = [T]>`.
    // Therefore, you can simply pass a `&Vec` to a function
    // that accepts `&[T]`.
    let _ = sum(&vector);

    let slice: &[i32] = &vector[..];
    let _ = sum(slice);
}

The example also shows that immutable (and mutable) slices implement IntoIterator, yielding references to each slice element.

String Slices

String slices, denoted &str, are very common. In particular, string literals are of type &'static str. String slices are essentially regular slices, with the additional requirement that they must always be valid UTF-8. Owned strings (String) dereference to &str, just like Vec<T> dereferences to &[T].

See the String chapter for more details.