🎓 A1Learner
🔍

Rust Splitting Text

split cuts text at every separator. Rust has a whole family for this, and each member differs in one small, predictable way.

split keeps the empty pieces

Two separators in a row, or one at the end, leave empty text between them:

let v: Vec<&str> = "a,b,,c,".split(',').collect();
println!("{:?}", v);

split_once cuts in two

Often you want exactly two pieces: a key and the rest. split_once cuts at the first match only, and hands back an Option:

println!("{:?}", "name=ferris=admin".split_once('='));
println!("{:?}", "name=ferris=admin".rsplit_once('='));
println!("{:?}", "plain".split_once('='));

splitn stops early

splitn(n, pat) gives at most n pieces, and the last piece keeps the unsplit rest:

let v: Vec<&str> = "a=b=c".splitn(2, '=').collect();
println!("{:?}", v);
let v: Vec<&str> = "a=b=c".rsplitn(2, '=').collect();
println!("{:?}", v);

rsplit reads from the end

rsplit gives the same pieces in reverse order. The classic use: the file name at the end of a path:

let file = "/home/ferris/file.txt".rsplit('/').next();
println!("{:?}", file);

split_inclusive keeps the separator

split drops the separator. split_inclusive leaves it stuck to the end of each piece:

let v: Vec<&str> = "one\ntwo\nthree".split_inclusive('\n').collect();
println!("{:?}", v);

split_terminator drops the empty tail

Text that ends with a separator is terminated, not separated. split_terminator skips the final empty piece that split would give:

let v: Vec<&str> = "A.B.".split('.').collect();
println!("split = {:?}", v);
let v: Vec<&str> = "A.B.".split_terminator('.').collect();
println!("terminator = {:?}", v);

Example

fn main() {
    let record = "ferris,admin,active";
    let fields: Vec<&str> = record.split(',').collect();
    println!("user = {} role = {}", fields[0], fields[1]);
    let (key, rest) = "path=/home/ferris/docs".split_once('=').unwrap();
    println!("key = {} rest = {}", key, rest);
    let file = "/home/ferris/docs/report.txt".rsplit('/').next().unwrap();
    println!("file = {}", file);
    let lines: Vec<&str> = "one\ntwo\n".split_inclusive('\n').collect();
    println!("lines = {:?}", lines);
}

Key points

  • split divides text into pieces at every separator, and each piece borrows from the original.
  • split keeps empty pieces.
  • A separator at the start or end makes an empty piece.
  • Two separators in a row make an empty piece too.
  • That empty piece is information, not a mistake.
  • Every split piece is a &str borrowed from the original text: splitting copies nothing
  • Use split_whitespace when empty pieces are never wanted.
  • split_whitespace splits on any run of spaces, tabs or new lines.
  • split_whitespace skips the gaps.
  • Use lines() for whole text with new lines: it also hides the \r of Windows-style \r\n line endings
  • splitn leaves the rest of the text whole in the second piece.

In this example, one line of comma-separated text is split into fields. The first two print as user and role. Next, split_once cuts "path=/home/ferris/docs" at its first = sign only, so rest keeps its slashes whole. The path is then read backwards with rsplit, whose first piece is the file name at the end. Finally, split_inclusive keeps each new line attached to its piece — the {:?} output shows the \n inside the quotes. Four members of one family, four slightly different cuts.

Note

splitn counts pieces, not cuts.

splitn(2, pat) cuts once.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Rust Playground

Exercise

What does "a=b=c".split_once('=') return?