str::replace¶
Level: reference · for working programmers
One line: A new String with every occurrence of the pattern replaced — the original is untouched, because a str cannot change length.
Stable since 1.0.0.
Every occurrence, not the first. replacen is the one that takes a limit.
It always allocates, even when nothing matched — the return type is String, so a no-op replace still copies the whole string. On a hot path where matches are rare, guard with contains, or use Cow to avoid the copy.
Chained replaces are not simultaneous, which is the classic bug:
fn main() {
// Meant to swap a and b. Does not.
println!("{}", "ab".replace('a', "b").replace('b', "a")); // aa
}
The first pass turns ab into bb, and the second turns both into aa. Anything that looks like a substitution table needs a single pass — build it by hand with match_indices, or use a crate.
The pattern is the usual four shapes, so replace(char::is_whitespace, "_") works. The replacement is always a plain &str — there are no capture groups; that is what a regex crate is for.
Example¶
str_replace.rs in full — pasted here by tools/run_examples.py from the file CI compiles and runs.
fn main() {
println!("{:?}", "this is old, very old".replace("old", "new"));
println!("{:?}", "a-b-c".replace('-', ""));
println!("{:?}", "a b\tc".replace(char::is_whitespace, "_"));
// The original is unchanged; a new String comes back.
let original = "keep me";
let changed = original.replace("keep", "drop");
println!("{original:?} {changed:?}");
// Chained replaces are sequential, not simultaneous.
println!("{:?}", "ab".replace('a', "b").replace('b', "a"));
// A single pass, which is what a swap actually needs.
let swapped: String = "ab".chars()
.map(|c| match c { 'a' => 'b', 'b' => 'a', other => other })
.collect();
println!("{swapped:?}");
// It allocates even when nothing matches.
let s = "no match here";
println!("{}", s.replace("zzz", "!") == s);
}
Verified output of str_replace.rs — regenerated by tools/run_examples.py, never hand-typed.
See also¶
str::replacen— the same, with a limitString::replace_range— in-place replacement of a byte rangestr::match_indices— a single pass you controlstr::contains— guarding the allocation
str::replace in the standard library ↗
Po polsku¶
Najczęstsze polskie zastosowanie tej metody to zdejmowanie ogonków — .replace('ą', "a").replace('ć', "c")… — i właśnie w takim łańcuchu wywołań opisana tu pułapka boli najbardziej. Zamiany nie dzieją się jednocześnie: każde wywołanie przerabia wynik poprzedniego, więc jeśli coś podmieniasz na literę podmienianą krok dalej, efekty skumulują się kaskadowo — pokazuje to "ab".replace('a', "b").replace('b', "a"), które daje "aa", a nie zamianę miejscami. Prawdziwa tabela podstawień wymaga jednego przejścia, na przykład chars().map(...) z matchem, jak w linii zwracającej "ba". Pamiętaj też, że każde wywołanie alokuje nowy String, nawet gdy nic nie pasowało — dziewięć polskich liter diakrytycznych to dziewięć kopii całego tekstu — i że replace podmienia wszystkie wystąpienia; od limitu jest replacen.
Szukaj po polsku: usuwanie polskich znaków diakrytycznych · zamiana fragmentu tekstu · rust str replace all occurrences · rust replace multiple patterns single pass