#233 Jul 1, 2026

233. str::split_terminator — Split Without the Trailing Empty String

"a.b.c.".split('.') hands you a phantom empty string at the end. split_terminator reads the separator as ending each piece, not delimiting it — so the trailing one just vanishes.

Split counts the region after the final separator, even when it’s empty. Feed it text that ends with the separator and you get a ghost element:

1
2
let parts: Vec<&str> = "a.b.c.".split('.').collect();
assert_eq!(parts, ["a", "b", "c", ""]);

That trailing "" is a landmine downstream: an empty record, a blank line, a zero-length field you didn’t ask for.

split_terminator treats the separator as terminating each piece, so a trailing separator produces no empty tail:

1
2
let parts: Vec<&str> = "a.b.c.".split_terminator('.').collect();
assert_eq!(parts, ["a", "b", "c"]);

It only drops the empty piece caused by a trailing separator — interior empties are still real data and still show up:

1
2
let parts: Vec<&str> = "a..b.".split_terminator('.').collect();
assert_eq!(parts, ["a", "", "b"]);

And with no trailing separator it behaves exactly like split:

1
2
let parts: Vec<&str> = "a.b.c".split_terminator('.').collect();
assert_eq!(parts, ["a", "b", "c"]);

Reach for it when parsing records that end with their delimiter — newline-terminated logs, ;-terminated statements, trailing-comma lists — and skip the .filter(|s| !s.is_empty()) cleanup.

← Previous 232. Iterator::partition — Split Into Two Collections in One Pass Next → 234. core::range::Range — A Range That's Copy, So You Can Store and Reuse It