Beginner · Borrowing, slices and lifetimes · topic 16 of 50 · 30 min
Slices and strings
Explanation
What is a slice?
A slice is a reference to a continuous part of a collection, instead of the whole thing. It is made of two numbers: a pointer to the first item and a length. A slice does not own anything; it borrows.
let s = String::from("Hello Delhi");
let hello: &str = &s[0..5];
let delhi: &str = &s[6..]; // from 6 to the end
s: ptr ---> H e l l o _ D e l h i
^ ^
hello: ptr -----+ len 5 |
delhi: ptr ---------------+ len 5Range shortcuts: [..3] means from the start, [3..] means to the end, [..] means everything.
String versus &str
| String | &str (string slice) | |
|---|---|---|
| Owns its data? | Yes, on the heap | No, it borrows |
| Can grow or change? | Yes (push, push_str) | No |
| Made by | String::from("x"), "x".to_string(), format! | "literal", &my_string, &my_string[0..3] |
| Use for | Storing and building text | Reading text, function parameters |
A string literal like "Mumbai" is a &str that points into the program's own binary file, so it lives for the whole run. Its full type is &'static str, which you will understand in the lifetimes lesson.
Prefer &str for parameters
fn first_word(s: &str) -> &str {
match s.find(' ') {
Some(i) => &s[..i],
None => s,
}
}
fn main() {
let owned = String::from("Jai Hind");
println!("{}", first_word(&owned)); // works with &String
println!("{}", first_word("Vande Mataram")); // and with literals
}UTF-8: why slicing counts bytes
Rust strings are always UTF-8, a way of storing any language's text where each character takes 1 to 4 bytes. English letters take 1 byte. Most Hindi (Devanagari) characters take 3 bytes. The rupee sign ₹ also takes 3 bytes.
let word = "नमस्ते";
println!("{}", word.len()); // 18 bytes
println!("{}", word.chars().count()); // 6 chars
// &word[0..1] -> panics: byte index 1 is not a char boundaryAlso note: what a reader sees as one letter can be several chars. "स्ते" in नमस्ते is made of several Unicode code points. For "visible letters" you need a crate such as unicode-segmentation; for most programs, working with whole words and chars is enough.
Array slices
fn average(marks: &[u32]) -> f64 {
let total: u32 = marks.iter().sum();
total as f64 / marks.len() as f64
}
fn main() {
let marks = [70, 80, 90, 60];
println!("{}", average(&marks)); // whole array: 75
println!("{}", average(&marks[1..3])); // 80 and 90: 85
let v = vec![50, 100];
println!("{}", average(&v)); // a Vec works too: 75
}Common mistakes
- Writing fn f(s: &String) for parameters. Use &str: it accepts more kinds of input.
- Indexing a string with s[0]. Rust does not allow it, because a byte may not be a whole character. Use s.chars().next().
- Assuming len() is the number of letters. It is the number of bytes.
- Returning a &str slice of a local String from a function. Return the String instead.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Slices and strings · 1/6
A slice is a reference to part of a collection. It is a pointer plus a length, and it owns nothing.
Prefer a video by a teacher? Find free videos on “rust slices string vs str utf-8 explained” on YouTube ↗
In real life: Showing initials for Indian names
A contacts app shows initials in a circle, like "RK" for Rahul Kumar, and also supports names typed in Hindi. Taking &name[0..1] would panic for "राहुल". The app uses split_whitespace and chars().next(), which always respect character boundaries, so it works for every script.
fn initials(name: &str) -> String {
name.split_whitespace()
.filter_map(|w| w.chars().next())
.collect()
}
fn main() {
println!("{}", initials("Rahul Kumar")); // RK
println!("{}", initials("राहुल कुमार")); // रक
}Practice (0/3 done)
What does this print? let s = "Chennai Express"; println!("{}", &s[8..]);
What is "₹500".len()?
Write fn last_word(s: &str) -> &str that returns the last word of a sentence (use rfind(' ') or split_whitespace().last()). Test it with an English sentence, a Hindi sentence and a single word. Then try &s[0..1] on a Hindi word and read the panic message.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- LifetimesBorrowing, slices and lifetimes · same module
- Arrays and slicesGo Programming (backend and cloud) · Collections and structs
- Strings: working with textPython Programming · Text and decisions
- Strings, bytes and runesGo Programming (backend and cloud) · Collections and structs
- Strings are char arraysC Programming (from zero to systems) · Strings in C
- Strings and template literalsJavaScript Programming (language in depth) · Strings, decisions and loops
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
What is the type of the literal "Goa"?
Answer: &str
String literals are &str (more exactly &'static str).
Which function signature accepts both a &String and a literal?
Answer: fn f(s: &str)
&String coerces to &str automatically, and literals already are &str.
What does len() return for a String?
Answer: Number of bytes
len() counts bytes of UTF-8 data.
Why can you not write my_string[0] in Rust?
Answer: A single byte may not be a whole character, so indexing is not allowed
Rust does not let you index strings by position, because bytes are not always characters.
What does a slice contain?
Answer: A pointer and a length
A slice is a fat pointer: where it starts and how many items.
Can you compare two slices with ==?Related
Answer: No, only a slice with nil
Slices are not comparable; use slices.Equal to compare contents.
From Arrays and slices
What does len("India 2024") give?Related
Answer: 10
India has 5 characters, the space is 1 and 2024 is 4 more: 5 + 1 + 4 = 10.
What type do you get from s[0] when s is a string?Related
How many bytes does "UPI" occupy as a string literal?Related
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 1 more topic in “Borrowing, slices and lifetimes” before its exam: Lifetimes.
- Module 4 exam: Borrowing, slices and lifetimes · 13 questions · 25 min, without help. 4 topics of this module still to finish first.
- Then the next module, Structs, enums and pattern matching. 50 topics left in the course before the final exam.