Skip to content

str::replace

str methods · Strings

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.

pub fn replace<P: Pattern>(&self, from: P, to: &str) -> String

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.

"this is new, very new"
"abc"
"a_b_c"
"keep me" "drop me"
"aa"
"ba"
true

See also

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