Rust: Slices

Home · Blog

3 February 2023

A slice, in Rust, is a reference to a part of an array. For example:

let a: [u32; 5] = [0, 1, 2, 3, 4];
let s: &[u32] = &a[1..4];
println!("{:?}", s);   // prints [1, 2, 3]

The slice refers to the part of the array with elements 1 up to (but not including) 4, using zero-based indexing. If you look at the type annotations closely, you can see that the array type includes the element type and a size, but the slice type doesn’t include a size. Its size is only known at runtime. Once you have a slice, you can index it, get its length, or create another slice from it:

println!("{}", s[0]);      // prints 1
println!("{}", s.len());   // prints 3, the number of elements
let t: &[u32] = &s[1..3];
println!("{:?}", t);       // prints [2, 3]

If you declare the slice with &mut, you can use it to modify the unterlying array (which must also be mutable):

let mut a: [u32; 5] = [0, 1, 2, 3, 4];
let s1: &mut [u32] = &mut a[0..2];
s1[0] = 99;
println!("{:?}", a);   // prints [99, 1, 2, 3, 4]

The .. syntax has some variations:

let s1: &[u32] = &a;         // the whole array
let s2: &[u32] = &a[..];     // also the whole array
let s3: &[u32] = &a[1..4];   // from element 1 up to (but excluding) 4
let s4: &[u32] = &a[2..];    // from element 2
let s5: &[u32] = &a[..3];    // up to (but excluding) element 3

Behind the scenes, a slice is pretty simple: a pointer to the data and a length. On a 64-bit architectures, it takes 16 bytes. Unlike in Go, a slice in Rust doesn’t have a capacity and you can’t append to it.

Since slices are a kind of reference, the usual restrictions apply: a slice can’t outlive the array it refers to, you can’t modify the array while there’s a slice that refers to it, and you can have either one mutable or any number of immutable slices for an array.

String slices

The string slice type &str is an important special case:

let string = String::from("voilà");
let s1: &str = &string;
let s2: &str = &s1[2..];
println!("{}", s1.len());   // prints 6, the length in bytes
println!("{}", s1);         // prints: voilà
println!("{}", s2);         // prints: ilà

A string slice is always a valid UTF-8 string. If you try to create an invalid one, e.g. using &string[..5] in the example, the program will panic at runtime.