Skip to content

String::with_capacity

String methods · Strings

Level: reference · for working programmers

One line: An empty String with room for at least n bytes reserved up front — one allocation instead of a doubling sequence.

pub fn with_capacity(capacity: usize) -> String

Stable since 1.0.0.

The string is still empty; only the buffer is bought. len() is 0 and capacity() is at least n.

Growing a String by pushing reallocates on a doubling schedule, and each reallocation copies everything written so far. Building a 1 KB string from new() costs several allocations and several copies; from with_capacity(1024) it costs one and none.

The capacity is in bytes, not characters. Sizing a buffer for 100 characters of non-ASCII text needs up to 400 bytes.

Reserving is not a promise the string will not grow — pushing past n reallocates normally. And over-reserving wastes memory until shrink_to_fit gives it back, so the honest use is a size you can actually estimate: a known record length, a file's byte count, input.len() for a transformation that roughly preserves size.

Example

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

fn main() {
    let s = String::with_capacity(32);
    println!("len {} capacity {}", s.len(), s.capacity());

    // No reallocation while it fits.
    let mut planned = String::with_capacity(32);
    for word in ["alpha ", "beta ", "gamma"] {
        planned.push_str(word);
        println!("len {:>2} capacity {:>2}", planned.len(), planned.capacity());
    }

    // Growing from nothing walks a doubling sequence instead.
    let mut grown = String::new();
    for word in ["alpha ", "beta ", "gamma"] {
        grown.push_str(word);
        println!("len {:>2} capacity {:>2}  (from new)", grown.len(), grown.capacity());
    }
    println!("equal contents: {}", planned == grown);

    // Bytes, not characters.
    let wide = "é".repeat(4);
    println!("4 chars need {} bytes", wide.len());

    // Sizing from the input is the honest estimate.
    let input = "the quick brown fox";
    let mut upper = String::with_capacity(input.len());
    upper.push_str(&input.to_ascii_uppercase());
    println!("{} / {}", upper.len(), upper.capacity());
}

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

len 0 capacity 32
len  6 capacity 32
len 11 capacity 32
len 16 capacity 32
len  6 capacity  8  (from new)
len 11 capacity 16  (from new)
len 16 capacity 16  (from new)
equal contents: true
4 chars need 8 bytes
19 / 19

See also

String::with_capacity in the standard library ↗

Po polsku

with_capacity kupuje bufor, a nie treść: łańcuch znaków jest nadal pusty (len 0), zmienia się wyłącznie pojemność (capacity). Zysk jest czysto wydajnościowy — rosnący String realokuje się według podwajania i za każdym razem przepisuje wszystko, co już w nim jest. W przykładzie oba przebiegi stoją obok siebie: startując od new() pojemność idzie 8 → 16 → 16, a przy with_capacity(32) przez cały czas zostaje 32 i nie ma ani jednej realokacji.

Dla polskiego czytelnika kluczowe jest jedno zdanie: pojemność liczy się w bajtach, nie w znakach. with_capacity(100) zamawia 100 bajtów, a nie miejsce na 100 znaków — każda polska litera z diakrytykiem zajmuje w UTF-8 dwa bajty, emoji albo znak CJK cztery, więc pole na nazwisko pełne ą, ę i ł potrzebuje mniej więcej dwa razy więcej miejsca, niż podpowiada intuicja; stąd cztery znaki é z przykładu to 8 bajtów. Rezerwacja nie jest też obietnicą, że łańcuch nie urośnie: zapis powyżej n realokuje najzwyczajniej w świecie, a rezerwacja z nadmiarem marnuje pamięć aż do shrink_to_fit. Uczciwe oszacowanie to takie, które naprawdę znasz — długość rekordu, rozmiar pliku w bajtach albo input.len() dla przekształcenia mniej więcej zachowującego długość.

Szukaj po polsku: pojemność łańcucha znaków w bajtach · realokacja i podwajanie bufora · rust String with_capacity · rust string capacity bytes not chars