How to split a string
Short answer: str::split takes a separator and returns an iterator of &str slices — text.split(',') for a fixed separator, text.split_whitespace() for words, text.split_once('=') when you only need the first cut.
From a fresh project — cargo new split-demo, then replace src/main.rs with each block below and run it with cargo run. The project name only shows up in cargo’s own output; nothing else on this page depends on it.
Splitting on a separator
Section titled “Splitting on a separator”fn main() { let line = "alpha,beta,gamma"; for field in line.split(',') { println!("{field}"); }}alphabetagammasplit is lazy. It hands back an iterator of slices borrowed from line, and no text is copied until something consumes the iterator. Ask for a Vec and the pieces land in one:
fn main() { let line = "alpha,beta,gamma"; let fields: Vec<&str> = line.split(',').collect(); println!("{fields:?}");}["alpha", "beta", "gamma"]The separator doesn’t have to be a single character. A &str of any length works, and so does a char array when there are several:
fn main() { let line = "alpha;beta,gamma"; let fields: Vec<&str> = line.split([',', ';']).collect(); println!("{fields:?}");}["alpha", "beta", "gamma"]split is a method on str, so a String can be split the same way. The slices borrow from it.
The empty pieces
Section titled “The empty pieces”split is mechanical: every match of the separator is a cut, and the piece between two cuts can be empty. A separator at the start or end of the string leaves an empty piece there too:
fn main() { let line = "alpha,,beta,"; for field in line.split(',') { println!("[{field}]"); }}[alpha][][beta][]For a CSV line that is usually right: an empty field should stay empty. When it isn’t, split_terminator drops the piece a trailing separator produces, and only that one:
fn main() { let line = "alpha,,beta,"; let fields: Vec<&str> = line.split_terminator(',').collect(); println!("{fields:?}");}["alpha", "", "beta"]Words instead of separators
Section titled “Words instead of separators”Splitting on whitespace is common enough to have its own method. split_whitespace skips runs of any whitespace — spaces, tabs, newlines — and never returns an empty piece:
fn main() { let line = " alpha beta\tgamma "; let words: Vec<&str> = line.split_whitespace().collect(); println!("{words:?}");}["alpha", "beta", "gamma"]It splits on Unicode whitespace, so a non-breaking space counts as one. The empty string shows the difference between the two methods: "".split(',').count() is 1, one empty piece, while "".split_whitespace().count() is 0.
Only the first cut
Section titled “Only the first cut”Config and header lines are key=value. split_once cuts at the first occurrence of the separator and returns Option<(&str, &str)>:
fn main() { let line = "name=Ada Lovelace"; match line.split_once('=') { Some((key, value)) => println!("key={key} value={value}"), None => println!("no = in the line"), }}key=name value=Ada LovelaceFirst occurrence only: "a=b=c".split_once('=') gives ("a", "b=c"), so a value that contains the separator survives. rsplit_once makes the same cut from the other end — "report.tar.gz".rsplit_once('.') gives ("report.tar", "gz") — and splitn(2, ':') yields at most two pieces, the second holding whatever is left.
One compile error waits for anyone who wants owned Strings out of split. The pieces are &str, and the compiler says so:
fn main() { let line = "alpha,beta,gamma"; let fields: Vec<String> = line.split(',').collect(); println!("{fields:?}");}error[E0277]: a value of type `Vec<String>` cannot be built from an iterator over elements of type `&str` --> src/main.rs:3:47 |3 | let fields: Vec<String> = line.split(',').collect(); | ^^^^^^^ value of type `Vec<String>` cannot be built from `std::iter::Iterator<Item=&str>` |help: the trait `FromIterator<&str>` is not implemented for `Vec<String>` but trait `FromIterator<String>` is implemented for it --> /rustc/b940084d7eb6a299eb4bfeb8e34901bc051e7ac4/library/alloc/src/vec/mod.rs:3993:0 = help: for that trait implementation, expected `String`, found `&str`note: the method call chain might not have had the expected associated types --> src/main.rs:3:36 |2 | let line = "alpha,beta,gamma"; | ------------------ this expression has type `&str`3 | let fields: Vec<String> = line.split(',').collect(); | ^^^^^^^^^^ `Iterator::Item` is `&str` herenote: required by a bound in `collect` --> /rustc/b940084d7eb6a299eb4bfeb8e34901bc051e7ac4/library/core/src/iter/traits/iterator.rs:2073:4
For more information about this error, try `rustc --explain E0277`.Map the slices into owned strings first — .map(String::from).collect() — or keep the &str slices when borrowing is enough.
Related
Section titled “Related”- What is the difference between String and &str? — what the pieces
splitreturns actually are - When to use String::from, to_string, or into? — turning slices into owned
Strings - How to format strings with println! and format! — the
{:?}that prints aVec - Working with text — the rest of the chapter