Skip to content

str::split

str methods · Strings

Level: reference · for working programmers

One line: An iterator over the pieces between the matches — n matches always yield n+1 pieces, which is where every surprising empty string comes from.

pub fn split<P: Pattern>(&self, pat: P) -> Split<'_, P>

Stable since 1.0.0.

split is mechanical, not editorial. It reports the gaps, and a gap with nothing in it is still a gap:

input split(',')
"a,b,c" ["a", "b", "c"]
"a,,c" ["a", "", "c"] — nothing between the two commas
",a" ["", "a"] — nothing before the first
"a," ["a", ""] — nothing after the last
"" [""] — one piece, no matches

None of that is the method being unhelpful; the count is forced by the arithmetic. If you want the empties gone, that is a different question and split_whitespace (for prose) or a .filter(|p| !p.is_empty()) (for anything else) answers it.

An empty pattern is the case that surprises everyone, and it is the same rule with nothing left to hide behind: it matches at every char boundary, so "abc".split("") is four matches and five pieces — ["", "a", "b", "c", ""]. Splitting on nothing works it through, including the boundaries it is not allowed to land on.

Use split for delimited data and split_whitespace for prose. Reaching for the wrong one silently shifts your columns: on "a,,c", dropping the empty makes column 3 become column 2, and nothing errors.

Pieces are borrowed from the original string; nothing is allocated. The pattern is the usual four shapes.

The return value is a lazy iterator, not a list, so println!("{:?}", s.split(":")) prints the Split struct — searcher internals and a cursor — rather than the pieces. .collect::<Vec<&str>>() or a for loop is what produces those; Inside a Split reads the struct field by field.

Example

str_split.rs in full — pasted here by tools/run_examples.py from the file CI compiles and runs.

fn main() {
    for input in ["a,b,c", "a,,c", ",a", "a,", ""] {
        println!("{:<7} -> {:?}", format!("{input:?}"), input.split(',').collect::<Vec<&str>>());
    }

    // n matches, n+1 pieces — always.
    let s = "the rain in spain";
    println!("{} matches, {} pieces", s.matches("in").count(), s.split("in").count());

    // The four pattern shapes.
    println!("{:?}", "a1b2c".split(char::is_numeric).collect::<Vec<&str>>());
    println!("{:?}", "a-b_c".split(&['-', '_'][..]).collect::<Vec<&str>>());

    // Dropping empties is your decision, not the method's.
    let row = "a,,c";
    println!("{:?}", row.split(',').filter(|p| !p.is_empty()).collect::<Vec<&str>>());
    println!("columns: {} kept, {} after filtering",
             row.split(',').count(),
             row.split(',').filter(|p| !p.is_empty()).count());
}

Verified output of str_split.rs — regenerated by tools/run_examples.py, never hand-typed.

"a,b,c" -> ["a", "b", "c"]
"a,,c"  -> ["a", "", "c"]
",a"    -> ["", "a"]
"a,"    -> ["a", ""]
""      -> [""]
3 matches, 4 pieces
["a", "b", "c"]
["a", "b", "c"]
["a", "c"]
columns: 3 kept, 2 after filtering

See also

str::split in the standard library ↗

Po polsku

split nie szuka „słów”, tylko zgłasza przerwy między dopasowaniami, a przerwa, w której nic nie ma, dalej jest przerwą. Stąd cała arytmetyka: n dopasowań to zawsze n+1 kawałków, więc "a,,c" daje ["a", "", "c"], ",a" daje ["", "a"], a pusty tekst "" daje [""] — jeden pusty kawałek, nie zero. To nie jest złośliwość metody, tylko konsekwencja liczenia.

Polskiego czytelnika najczęściej myli tu nawyk z Pythona: tamtejsze bezargumentowe s.split() dzieli po białych znakach i samo wyrzuca puste kawałki, więc łatwo oczekiwać tego samego po str::split(','). Odpowiednikiem bezargumentowego split() jest split_whitespace, a nie split. Pomyłka nie kończy się błędem kompilacji ani paniką — cicho przesuwa kolumny: w "a,,c" po odfiltrowaniu pustych zostają 2 kawałki zamiast 3 i trzecia kolumna staje się drugą. Zasada jest prosta: split do danych z separatorem, split_whitespace do prozy, a wyrzucenie pustych to twoja świadoma decyzja (.filter(|p| !p.is_empty())).

Ostatnia rzecz, która zaskakuje przy pierwszym println!: split zwraca leniwy iterator, więc {:?} wypisze strukturę Split wraz z wnętrznościami wyszukiwarki, a nie kawałki. Dopiero collect::<Vec<&str>>() albo pętla for je pokazuje — i są to wycinki (&str) pożyczone z oryginalnego tekstu, bez żadnej alokacji.

Szukaj po polsku: dzielenie łańcucha znaków w Ruscie · puste elementy po podziale · rust split empty strings · rust split vs split_whitespace