Splitting a string into pieces
Splitting cuts a string at every separator and gives you the pieces between them. In Rust, split is a method on str that takes the separator:
let sentence = "hello world foo";
let words: Vec<&str> = sentence.split(" ").collect();
println!("{:?}", words);
["hello", "world", "foo"]
Two things in that code deserve a closer look: why collect() is needed, and what the type Vec<&str> means.
Why collect() is needed
split doesn't return a vector. It returns an iterator, a lazy object that produces the pieces one at a time as you ask for them. Nothing is cut until you consume it. collect() consumes the iterator and gathers its items into a collection.
Which collection? collect() can build many, so Rust needs to be told. Here the annotation on words does it: Vec<&str>. Without any type information, the compiler reports that it can't infer the type. You can also give the type to collect itself: sentence.split(" ").collect::<Vec<&str>>().
If you only need to walk through the pieces once, skip collect and loop over the iterator directly:
for w in sentence.split(" ") {
println!("{}", w);
}
What Vec<&str> means
Each piece is a &str, a borrowed view into the original string. No new strings are allocated for the pieces, which makes splitting cheap. The price is that the pieces can't outlive sentence. If you need strings that stand on their own, convert each piece, for example with .map(|w| w.to_string()) before collecting into a Vec<String>.
Separators and their quirks
The separator can be a string slice or a single char, so split(',') works for comma-separated text. Keep these behaviours in mind:
- Consecutive separators produce empty pieces.
"a b c".split(" ")yields["a", "", "b", "", "", "c"], because there is an empty string between every two adjacent spaces. - The same goes for separators at the ends.
"a,b,,c".split(',')yields["a", "b", "", "c"]. - An empty input still yields one piece.
"".split(" ")gives[""], a vector with one empty string, not an empty vector.
split_whitespace
For splitting text into words, split_whitespace() is usually the better tool. It splits on any run of whitespace (spaces, tabs, newlines) and ignores leading and trailing whitespace, so it never produces empty pieces:
let words: Vec<&str> = "a b c".split_whitespace().collect();
println!("{:?}", words);
let none: Vec<&str> = "".split_whitespace().collect();
println!("{:?}", none);
["a", "b", "c"]
[]
For the single-spaced sentence in this lesson, split(" ") and split_whitespace() give the same result. They differ as soon as the spacing is irregular.
Other ways to split
Besides split and split_whitespace, a few related methods are worth knowing:
lines()splits on line breaks and is the right choice for multi-line text.splitn(n, sep)stops afternpieces, leaving the rest of the string in the last one.split_once(sep)cuts at the first separator only and returns anOptionholding the two halves, which suitskey=valuetext:"name=Ana".split_once('=')isSome(("name", "Ana")).chars()splits a string into characters rather than by a separator.
A Vec lets you use the pieces by position. words[0] is the first word, words.len() is how many there are, and indexing past the end panics, so use words.get(i) when the position might not exist.
The exercise
The starting code contains only the loop that prints each word, so the variables have to be added above it:
- Declare
sentencewithlet, holding"hello world foo". - Declare
wordswith the typeVec<&str>. - Build it by calling
.split(" ")onsentenceand finishing with.collect().
let sentence = "hello world foo";
let words: Vec<&str> = sentence.split(" ").collect();
for w in &words {
println!("{}", w);
}
The loop borrows the vector with &words, so words is still usable afterwards. Looping over words without the & would move the vector into the loop. Each w is a &&str here, and println! prints it like a plain string. The output is one word per line:
hello
world
foo
Key takeaways
splitreturns a lazy iterator, andcollect()gathers it into a collection chosen by the type annotation.- The pieces of a
Vec<&str>borrow from the original string, so nothing is copied. - Repeated separators give empty pieces;
split_whitespace()avoids them. - Splitting an empty string with
splitgives one empty piece. - Loop over
&wordsto keep the vector usable afterwards.
