Rust The Slice Type

A slice is a reference to a part of a collection. Instead of copying data, a slice points to a section of an existing array or string. Slices let you work with portions of data efficiently without needing to own the entire thing.

String Slices

A string slice is a reference to part of a String. The type is written as &str.

let sentence = String::from("hello world");

let hello = &sentence[0..5];   ← Slice: characters 0 to 4
let world = &sentence[6..11];  ← Slice: characters 6 to 10

println!("{} {}", hello, world);

Output:

hello world

Range Syntax

The range [start..end] includes the character at start and stops before end. You can shorten the range when starting from zero or going to the end:

let s = String::from("hello");

let slice1 = &s[0..3];  ← "hel"
let slice2 = &s[..3];   ← Same thing: start from beginning
let slice3 = &s[2..5];  ← "llo"
let slice4 = &s[2..];   ← From index 2 to end: "llo"
let slice5 = &s[..];    ← Entire string

The Ruler Diagram

sentence: h  e  l  l  o     w  o  r  l  d
index:    0  1  2  3  4  5  6  7  8  9  10

&sentence[0..5]  →  h e l l o        (indices 0,1,2,3,4)
&sentence[6..11] →  w o r l d        (indices 6,7,8,9,10)

Why Slices Are Useful

Consider a function that finds the first word in a sentence. Without slices, you might return the index where the word ends — but that index becomes meaningless if the original string changes later. A slice solves this because it holds a direct reference to the actual characters.

fn first_word(s: &String) -> &str {
    let bytes = s.as_bytes();

    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {          ← Found a space
            return &s[0..i];      ← Return slice up to the space
        }
    }

    &s[..]   ← No space found: return the whole string
}

fn main() {
    let sentence = String::from("hello world");
    let word = first_word(&sentence);
    println!("First word: {}", word);
}

Output:

First word: hello

How the Slice Stays Valid

The slice borrows part of sentence. Rust's borrow checker ensures sentence cannot be modified or dropped while the slice is still in use. This prevents bugs where a reference points to changed or freed data.

String Literals Are Slices

When you write a string literal in your code, it is stored directly in the program binary. Its type is &str — an immutable string slice pointing into the binary. This is why string literals are always valid for the life of the program.

let greeting = "Hello, world!";
//              ↑
// Type: &str — a slice pointing to text in the binary

String vs &str

String            ← Owned, heap-allocated, can grow and shrink
&String           ← Reference to a whole String
&str              ← Slice: reference to part (or all) of any string data

Functions that only need to read a string should accept &str instead of &String. A &str works with both String values and string literals, making functions more flexible.

Array Slices

Slices work on arrays too, not just strings. The type is written as &[T] where T is the element type:

fn sum(numbers: &[i32]) -> i32 {
    let mut total = 0;
    for n in numbers {
        total += n;
    }
    total
}

fn main() {
    let all = [10, 20, 30, 40, 50];

    let total_all = sum(&all);          ← Pass the whole array
    let total_part = sum(&all[1..4]);   ← Pass elements 1, 2, 3

    println!("Total: {}", total_all);   ← 150
    println!("Part:  {}", total_part);  ← 90
}

The Window Diagram

all:  [ 10 | 20 | 30 | 40 | 50 ]
               ↑         ↑
        &all[1..4] is a window showing [ 20 | 30 | 40 ]
        The data is not copied — the slice just points into it.

Key Properties of Slices

Property             Detail
---------            ------
Ownership            None — slices borrow from an owner
Mutability           Can be &[T] (read only) or &mut [T] (read-write)
Length               Tracked automatically inside the slice
Bounds checking      Accessing out-of-bounds panics at runtime

Bounds Checking

If you try to access a slice index that does not exist, Rust panics with a clear message instead of silently reading bad memory:

let numbers = [1, 2, 3];
let bad = &numbers[1..10];   ← Panic: index out of bounds

This is safer than C, where an out-of-bounds access reads random memory without any warning.

Quick Reference

&s[..]          ← Slice of entire string s
&s[0..5]        ← Slice of first 5 characters
&s[3..]         ← Slice from index 3 to end
&arr[1..4]      ← Slice of array elements 1, 2, 3
&str            ← String slice type
&[i32]          ← Integer array slice type

Leave a Comment