Skip to content

String::push

String methods · Strings

Level: reference · for working programmers

One line: Appends one char, encoding it as 1–4 UTF-8 bytes — so len() can grow by more than one.

pub fn push(&mut self, ch: char)

Stable since 1.0.0.

The argument is a char, not a &str, so it takes single quotes: s.push('!'). Appending text uses push_str, and s.push("!") is a type error people meet early.

len() grows by c.len_utf8(), which is 1 for ASCII and up to 4 otherwise. So a loop that pushes n characters does not produce a string of length n.

Amortized O(1): the buffer doubles when it fills, so a run of pushes is cheap on average even though individual ones reallocate.

Building a String character by character is idiomatic, but chars().map(..).collect::<String>() usually reads better and sizes the buffer more sensibly.

Example

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

fn main() {
    let mut s = String::from("hi");
    s.push('!');
    println!("{s:?}");

    // A char is 1..=4 bytes, so len grows by more than one.
    let mut wide = String::new();
    for c in ['a', 'é', '👋'] {
        wide.push(c);
        println!("pushed {c:?} -> len {} (grew by {})", wide.len(), c.len_utf8());
    }

    // push takes a char; push_str takes text.
    let mut t = String::new();
    t.push('a');
    t.push_str("bc");
    println!("{t:?}");

    // Growth doubles, so a run of pushes is cheap on average.
    let mut grown = String::new();
    let mut capacity = grown.capacity();
    for i in 1..=9 {
        grown.push('x');
        if grown.capacity() != capacity {
            capacity = grown.capacity();
            println!("push {i} reallocated: len {} capacity {capacity}", grown.len());
        }
    }

    // collect is usually the better spelling.
    let shouted: String = "hi".chars().map(|c| c.to_ascii_uppercase()).collect();
    println!("{shouted:?}");
}

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

"hi!"
pushed 'a' -> len 1 (grew by 1)
pushed 'é' -> len 3 (grew by 2)
pushed '👋' -> len 7 (grew by 4)
"abc"
push 1 reallocated: len 1 capacity 8
push 9 reallocated: len 9 capacity 16
"HI"

See also

String::push in the standard library ↗

Po polsku

Najczęstsza wywrotka na tej metodzie jest składniowa, nie pojęciowa: push przyjmuje pojedynczy znak (char), więc pisze się s.push('!') w apostrofach — s.push("!") w cudzysłowie to E0308, mismatched types, bo od tekstu jest osobne push_str. Druga rzecz to arytmetyka długości: len() rośnie o c.len_utf8(), czyli o 1 dla ASCII, ale o 2 dla é, ą czy ż i o 4 dla emoji, co strona pokazuje ciągiem len 1, len 3, len 7 po trzech dołożeniach — pętla dokładająca n znaków nie daje więc łańcucha o długości n. Koszt jest zamortyzowany O(1), bo bufor podwaja się dopiero, gdy się zapełni (stąd push 1 reallocated ... capacity 8 i push 9 ... capacity 16), ale jeśli budujesz łańcuch znak po znaku z iteratora, czytelniejsze i rozsądniej dobierające rozmiar bufora bywa chars().map(..).collect::<String>().

Szukaj po polsku: znak a łańcuch znaków · apostrof czy cudzysłów · rust push char vs push_str · rust E0308 expected char found &str · rust collect into String