SyntaxStudy
Sign Up
Rust Slices and the Borrow Checker
Rust Beginner 1 min read

Slices and the Borrow Checker

A slice is a reference to a contiguous sequence of elements in a collection. String slices (`&str`) and array slices (`&[T]`) are the most common forms. Slices are fat pointers: they carry both a pointer to the first element and the length of the slice. Because a slice is a borrow, the borrow checker ensures that the underlying data cannot be mutated or dropped while the slice is live. String slices arise naturally when you work with `String`: `&s[0..5]` borrows the first five bytes of `s`. The Rust standard library heavily uses `&str` as the preferred type for read-only string data because it works for both owned `String` values and string literals without copying. Accepting `&str` in a function parameter is more flexible than accepting `&String`. Array and vector slices enable you to write functions that work on any contiguous sequence regardless of whether the data is a stack-allocated array or a heap-allocated `Vec`. The slice type `&[T]` abstracts over both. The borrow checker's guarantee that a slice reference keeps the backing allocation alive prevents the common C bug of retaining a pointer to a deallocated buffer.
Example
fn first_word(s: &str) -> &str {
    let bytes = s.as_bytes();
    for (i, &byte) in bytes.iter().enumerate() {
        if byte == b' ' {
            return &s[..i];
        }
    }
    s   // whole string is one word
}

fn sum_slice(nums: &[i32]) -> i32 {
    nums.iter().sum()
}

fn main() {
    // String slices
    let sentence = String::from("hello world");
    let word = first_word(&sentence);
    println!("first word = {word}");
    // sentence.clear(); // compile error: mutable borrow while 'word' is live

    // &str works for both String refs and literals
    let literal = "foo bar baz";
    println!("first word of literal = {}", first_word(literal));

    // Array slice
    let arr = [1, 2, 3, 4, 5];
    println!("sum of arr = {}", sum_slice(&arr));

    // Vec slice — &[T] accepts both arrays and vecs
    let v: Vec<i32> = vec![10, 20, 30];
    println!("sum of vec = {}", sum_slice(&v));

    // Mutable slice
    let mut data = [3, 1, 4, 1, 5, 9, 2, 6];
    data.sort();
    println!("sorted = {:?}", &data);

    // Sub-slice
    let middle = &data[2..6];
    println!("middle slice = {:?}", middle);
}