🎓 A1Learner
🔍

Rust Slices

A slice is a borrowed view of part of a value. A slice stores a start position and a length.

Cut a piece of text

&s[0..5] is a view of s from position 0 up to, but not including, 5. A slice stores where it starts and how long it is — no copy is made:

let s = String::from("hello world");
let hello = &s[0..5];
println!("{hello} ({} letters)", hello.len());

Four ways to write the range

..b starts at the beginning, a.. runs to the end, a..=b includes both ends, and .. alone takes the whole run:

let a = [10, 20, 30, 40];
println!("{:?}", &a[1..3]);
println!("{:?}", &a[..2]);
println!("{:?}", &a[2..]);
println!("{:?}", &a[0..=1]);
println!("{:?}", &a[..]);

One function for wholes and parts

A parameter typed &[i32] accepts the whole array and any middle section alike:

fn total(nums: &[i32]) -> i32 { nums.iter().sum() }
let a = [1, 2, 3, 4];
println!("{} {}", total(&a), total(&a[1..3]));

Overlapping windows

windows(n) slides a view of length n along the slice, one step at a time:

let a = [1, 2, 3];
for w in a.windows(2) {
    println!("{:?}", w);
}

Chunks, and a clean split

chunks(n) cuts non-overlapping pieces — the last may be shorter. split_at(i) cuts one slice into two halves at position i. split_at_mut does the same job, for mutable borrows of two parts at once:

let a = [1, 2, 3, 4];
for c in a.chunks(3) { println!("{:?}", c); }
let halves = a.split_at(2);
println!("{:?} {:?}", halves.0, halves.1);

An empty slice is allowed

&a[0..0] has length 0. Code that loops over it simply runs zero times:

let a = [1, 2, 3];
let none = &a[0..0];
println!("empty len = {}", none.len());

Example

fn first_word(s: &str) -> &str {
    let bytes = s.as_bytes();
    let mut i = 0;
    while i < bytes.len() {
        if bytes[i] == b' ' {
            return &s[..i];
        }
        i += 1;
    }
    s
}
fn main() {
    let sentence = String::from("fearless concurrency");
    println!("{}", first_word(&sentence));
    println!("{}", first_word("hello world"));
}

Key points

  • While a slice is in use, the original value cannot be changed
  • Text slices must begin and end on letter boundaries.
  • Cutting inside a multi-byte letter (like é) makes the program panic.
  • The cut would not be valid text.
  • A slice cannot grow: it stores only a start and a length, and knows nothing about the data around it.
  • For a bigger view, go back to the original value and cut a new, wider slice from it

In this example, first_word takes a &str and gives back a &str — a view, not a copy. It walks the bytes of the text with as_bytes. When it finds the space byte, it returns &s[..i], the slice before the space. If there is no space, it returns the whole text. main calls it with a String (borrowed as text) and gets "fearless". Then it calls it with a plain literal and gets "hello". One function serves both, because it asked for a slice.

Note

Prefer &str and &[T] in function parameters over &String and &Vec<T>.

A function that takes a slice accepts whole values and parts of values alike.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Rust Playground

Exercise

What two things does a slice store?