#205 Jun 16, 2026

205. strip_prefix / strip_suffix — Remove a Prefix Once, Not Every Repeat

Reaching for trim_start_matches to peel off a "--" or a leading slash? It strips every repeated match and silently does nothing when there’s no match. strip_prefix removes exactly one and tells you whether it hit.

The Problem

trim_start_matches keeps eating as long as the pattern matches, which is rarely what “remove the prefix” means:

1
2
3
4
5
6
7
let path = "/////etc";

// Strips ALL leading slashes — not just one
assert_eq!(path.trim_start_matches('/'), "etc");

// And with a repeated substring it does the same
assert_eq!("foofoobar".trim_start_matches("foo"), "bar");

It also can’t tell you whether anything was removed — a non-match returns the string unchanged, so you can’t branch on it.

The Fix: strip_prefix

strip_prefix removes one occurrence and returns an Option: Some(rest) on a hit, None when the prefix isn’t there.

1
2
3
4
5
let path = "/////etc";

assert_eq!(path.strip_prefix('/'), Some("////etc")); // exactly one
assert_eq!("foofoobar".strip_prefix("foo"), Some("foobar"));
assert_eq!("foobar".strip_prefix("xyz"), None);       // no match, told you so

The Option is the real win: it doubles as a “did this start with the prefix?” test. Parsing a CLI flag becomes a one-liner:

1
2
3
4
5
6
fn flag_value(arg: &str) -> Option<&str> {
    arg.strip_prefix("--")
}

assert_eq!(flag_value("--verbose"), Some("verbose"));
assert_eq!(flag_value("positional"), None);

Its Mirror: strip_suffix

Same deal at the other end — perfect for trimming a known extension or unit without slicing indices by hand:

1
2
3
4
5
6
fn without_ext(name: &str) -> &str {
    name.strip_suffix(".rs").unwrap_or(name)
}

assert_eq!(without_ext("main.rs"), "main");
assert_eq!(without_ext("README"), "README"); // unchanged, no panic

Use trim_start_matches / trim_end_matches only when you genuinely want to collapse a run of repeats. For peeling one known prefix or suffix — and knowing if it was there — strip_prefix and strip_suffix say exactly what you mean.

← Previous 204. take_while / skip_while — Act on the Leading Run, Not Every Match Next → 206. trim_ascii — Trim Whitespace From &[u8] Without Going Through str