RustArena

Step 3 of 7

Splitting: lines, split, splitn, split_once

Most text a program receives is structured: lines, fields separated by commas or tabs, key=value pairs. Splitting is how you take it apart.

fn main() {
    let order = "A12,Crab plush,4\nB07,Rust mug,12\nC33,Sticker pack,1";
    let mut total = 0;
    for line in order.lines() {
        let fields: Vec<&str> = line.split(',').collect();
        let qty: u32 = fields[2].parse().expect("qty must be a number");
        total += qty;
        println!("{} x {}", qty, fields[1]);
    }
    println!("{total} items");
}
4 x Crab plush
12 x Rust mug
1 x Sticker pack
17 items

The splitting family

fn main() {
    println!("{:?}", "a,,b".split(',').collect::<Vec<&str>>());
    println!("{:?}", "  a   b ".split_whitespace().collect::<Vec<&str>>());
    println!("{:?}", "ERROR: disk full: /var".splitn(2, ": ").collect::<Vec<&str>>());
    println!("{:?}", "port=8080".split_once('='));
    println!("{:?}", "report.final.pdf".rsplit_once('.'));
    println!("{:?}", "a;b,c".split([',', ';']).collect::<Vec<&str>>());
}
["a", "", "b"]
["a", "b"]
["ERROR", "disk full: /var"]
Some(("port", "8080"))
Some(("report.final", "pdf"))
["a", "b", "c"]
Method Splits Notes
split(sep) at every separator keeps empty pieces ("a,,b" has an empty middle field)
split_whitespace() at any run of spaces, tabs, newlines never gives empty pieces
lines() at line endings
splitn(n, sep) at most n pieces the last piece keeps any further separators
split_once(sep) at the first separator Option<(&str, &str)>: None if there's none
rsplit_once(sep) at the last separator file extensions, host:port

The separator can be a char, a &str, an array of chars ([',', ';'], any of them) or a closure (|c: char| !c.is_alphanumeric()). collect::<Vec<&str>>() names the type in place (the "turbofish" ::<>) when there's no variable to annotate.

Common mistakes

Your task

Write fn parse_product(line: &str) -> Option<Product> for lines like A12, Crab plush, 4 (SKU, name, quantity). Trim spaces around each field. Return None unless there are exactly three fields and the quantity is a valid u32. Tip: let [sku, name, qty] = fields.as_slice() else { return None; };

Challenge 2

Write fn extension(filename: &str) -> Option<&str> that returns the text after the last dot: "photo.JPG" gives Some("JPG") and "archive.tar.gz" gives Some("gz"). Return None when there's no dot, when the name starts with its only dot (hidden files like .bashrc have no extension), or when the dot is the last character.

Challenge 3

Read a CSV grade sheet from input. The first line is the header name,score; every other line should be name,score. Trim spaces around each field. Skip rows that don't have exactly two fields (an empty line counts as one such row) or whose score isn't a whole number. There's always at least one valid row. Print:

students: 3
average: 84.7
best: ana (91)
skipped: 1

The average has one decimal. If two students share the best score, report the first one.

Previous: Slicing safely at char boundaries Next: Trimming and searching