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.
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.