Skip to content

String::replace_range

String methods · Strings

Level: reference · for working programmers

One line: Replaces a byte range with different text, in place — and the replacement need not be the same length.

pub fn replace_range<R>(&mut self, range: R, replace_with: &str)
where
    R: RangeBounds<usize>,

Stable since 1.27.0.

This is the in-place counterpart of str::replace, which always allocates a new String. Here the existing buffer is reused when it can be, and the tail shifts to make room or close the gap.

The range panics on the usual two conditions. The replacement is any &str, including "" — which makes it a deletion, the same as drain without reading the result.

Because lengths can differ, offsets after the range are invalidated. Editing several ranges therefore has to go back to front, so that the offsets you have not used yet still refer to the same text — which is exactly what rmatch_indices is for.

Example

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

fn main() {
    let mut s = String::from("hello world");
    s.replace_range(0..5, "goodbye");
    println!("{s:?}");

    // The replacement can be shorter, longer, or empty.
    let mut t = String::from("a-b-c");
    t.replace_range(1..2, "");
    println!("{t:?}");

    // Several edits, back to front, so the offsets stay valid.
    let text = "one,two,three";
    let mut owned = String::from(text);
    for (i, m) in text.rmatch_indices(',') {
        owned.replace_range(i..i + m.len(), " and ");
    }
    println!("{owned:?}");

    // Front to back with stale offsets: the same loop, wrong answer.
    let mut broken = String::from(text);
    for (i, m) in text.match_indices(',') {
        if i + m.len() <= broken.len() && broken.is_char_boundary(i) {
            broken.replace_range(i..i + m.len(), " and ");
        }
    }
    println!("{broken:?}");
}

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

"goodbye world"
"ab-c"
"one and two and three"
"one and and two,three"

See also

String::replace_range in the standard library ↗

Po polsku

replace_range podmienia zakres bajtów w miejscu, wykorzystując istniejący bufor, podczas gdy str::replace zawsze alokuje nowy łańcuch znaków i podmienia wszystkie wystąpienia — na tej różnicy opiera się wybór między nimi. Nowy tekst nie musi mieć długości usuwanego (pusty "" zamienia tę metodę w usuwanie zakresu), a stąd bierze się jedyna naprawdę groźna konsekwencja: każde przesunięcie za zmienianym zakresem przestaje być aktualne. Dlatego serię podmian wykonuje się od końca ku początkowi, i właśnie do tego służy rmatch_indices — przykład na tej stronie stawia oba wyniki obok siebie: poprawny "one and two and three" i ten sam kod idący od przodu po nieaktualnych przesunięciach, czyli "one and and two,three". Poza tym obowiązują zwykłe warunki paniki: zakres poza łańcuchem albo koniec trafiający w środek znaku, co przy polskich literach zdarza się szybciej, niż się spodziewasz.

Szukaj po polsku: podmiana zakresu w miejscu · nieaktualne przesunięcia · edycja od końca łańcucha · rust String replace_range · rust replace vs replace_range