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?