Slices
Prerequisites: References & borrowing
A slice is a reference to a contiguous part of a collection, rather than the whole thing. Slices don't own data; they borrow it.
Syntax
let s = String::from("hello world");
let hello: &str = &s[0..5]; // string slice
let world: &str = &s[6..]; // from index 6 to the end
let numbers = [10, 20, 30, 40];
let middle: &[i32] = &numbers[1..3]; // array slice: [20, 30]
Range forms work the same way for strings and arrays:
a..b— fromaup to, but not including,ba..— fromato the end..b— from the start up tob..— the whole thing
String slices (&str). A string literal like "hi" is itself a
&str — a slice pointing into the program's binary. String slice
indexes are byte positions, not character positions.
Why &str makes better parameters. A function that takes &str
accepts both string literals and &String values — Rust converts
&String to &str automatically. The same goes for &[T]: a function
taking &[i32] accepts a reference to an array or (later) a vector.
Example
fn first_word(text: &str) -> &str {
for (i, byte) in text.bytes().enumerate() {
if byte == b' ' {
return &text[..i];
}
}
text
}
fn sum(values: &[i32]) -> i32 {
let mut total = 0;
for v in values {
total += v;
}
total
}
fn main() {
let sentence = String::from("hello world");
let hello = &sentence[0..5];
let world = &sentence[6..];
println!("{hello} | {world}");
println!("first word: {}", first_word(&sentence));
println!("first word: {}", first_word("rust is fun"));
let numbers = [10, 20, 30, 40, 50];
let middle = &numbers[1..4];
println!("{middle:?}");
println!("len {}, sum {}", middle.len(), sum(middle));
println!("sum of all: {}", sum(&numbers));
}
text.bytes() goes through the string byte by byte, and b' ' is
the byte value of a space.
Expected output
hello | world
first word: hello
first word: rust
[20, 30, 40]
len 3, sum 90
sum of all: 150
Common mistakes
- Changing the data while a slice borrows it:
let mut s = String::from("hello world");
let word = &s[..5];
s.clear();
println!("{word}");
error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
This is the borrowing rules protecting you: word would point to
text that no longer exists.
- Slicing past the end.
&s[0..20]on an 11-byte string compiles, but panics at runtime with a message reporting that byte index 20 is past the end of the string. - Writing
&Stringor&[i32; 5]parameters where&stror&[i32]would do. The slice types accept more kinds of arguments.
Practice
Write a function largest(values: &[i32]) -> i32 that returns the
biggest number in a (non-empty) slice. Call it with the array
[3, 9, 2, 7] and print Largest: 9.
Show solution
fn largest(values: &[i32]) -> i32 {
let mut max = values[0];
for &v in values { // `&v` copies each i32 out of its reference
if v > max {
max = v;
}
}
max
}
fn main() {
let numbers = [3, 9, 2, 7];
println!("Largest: {}", largest(&numbers));
}