Skip to content

String methods

Level: reference · for working programmers

One line: One page per method on String — all 42 that stable Rust 1.98 exposes — each with the signature, the behaviour at the edges, and a runnable program whose output CI checks.

String owns its text and can grow it. That is the whole difference from str, and it is why this list is short: everything about reading text is over there, reached automatically because String dereferences to str. What is here is the part that needs ownership — building, growing, removing, and handing the allocation somewhere else.

So if you are looking for split, trim, find, replace or parse, they are on the str side and they work on a String unchanged.

Same page shape as the str reference: summary, signature, stability, prose, then a complete program and its verified output.

The three numbers

Every method below moves one of three numbers, and confusing them is most of the difficulty:

what it is changed by worked example
len() bytes written push, push_str, truncate, clear, pop, … len
capacity() bytes the buffer can hold reserve, shrink_to_fit, and growth capacity
chars().count() Unicode scalars nothing directly — it is derived by walking str::chars

The last column is a page whose program prints that number moving, compiled and run by CI, so you can watch the row happen rather than take it on trust. The len page covers all three in six lines of output — len 6 chars 5, then len 6 capacity 64. For the bytes-per-character half on its own, push pushes 'a', 'é' and '👋' and prints the length after each: 1, 3, 7. For the growth curve, capacity watches the buffer go 0 → 8 → 16 → 32 under three push_str calls.

len is part of the value; capacity is not. Two strings with the same text and different capacities are equal, hash the same, and print the same — so never assert on a capacity in a test.

Byte offsets panic in two ways

insert, remove, truncate, drain, replace_range, split_off and extend_from_within all take byte offsets, and all panic on the same two conditions: out of range, or inside a character. is_char_boundary is the test and floor_char_boundary is the repair — all seven of those examples call the test, and truncate's does the repair: a byte budget of 2 lands inside the é of "héllo wörld", and flooring it first truncates to "h" instead of panicking.


Making one

method what it does
new Empty, and allocates nothing until the first push
with_capacity Empty, with the buffer bought up front
from_utf8 Validate a Vec<u8> — no copy, and the Vec comes back on failure
from_utf8_lossy Bad bytes become ; returns a Cow, so clean input is free
from_utf16 Decode UTF-16 code units; fails on an unpaired surrogate
from_utf16_lossy The same, replacing instead of failing
from_utf16le Decode little-endian UTF-16 bytes
from_utf16le_lossy The same, replacing instead of failing
from_utf16be Decode big-endian UTF-16 bytes
from_utf16be_lossy The same, replacing instead of failing

Growing it

len is what you wrote; capacity is what you paid for.

method what it does
push One char — which is 1–4 bytes, so len grows by more than one
push_str Text on the end — the workhorse
insert One char at a byte offset, shifting the rest — O(n)
insert_str Text at a byte offset — prepending always costs a copy
extend_from_within Append a copy of one of its own ranges, no temporary
capacity Bytes the buffer holds — not part of the value
reserve Room for n more bytes — a headroom, not a total
reserve_exact The same without the round-up
try_reserve reserve that returns Result instead of aborting
try_reserve_exact Exact, and reporting
shrink_to_fit Give the spare capacity back — usually by copying
shrink_to Shrink toward a floor you name, for a reused buffer

Taking things out

method what it does
pop The last char — O(1), returns Option, cannot panic
remove The char at a byte offset — O(n), and it panics
truncate Drop everything past an offset — panics off a boundary
clear Empty it and keep the buffer — the reusable-buffer idiom
drain Remove a range and read what went
retain Keep what a predicate approves — one linear in-place pass
replace_range Swap a range for different text, in place
split_off Cut at an offset and get the tail as an owned String

Looking at it, and giving it away

The first four borrow; the rest consume.

method what it does
len The byte count
is_empty len() == 0 — capacity is irrelevant
as_str The whole thing as &str — usually supplied by deref coercion
as_mut_str As &mut str — mutable, but length-preserving
as_bytes The UTF-8 bytes, borrowed
into_bytes The Vec<u8>, owned — no copy, no UTF-8 promise
into_boxed_str A Box<str> — shrinks, and drops the capacity field
into_raw_parts Pointer, length, capacity — and the destructor stops running
leak A &'static mut str that is never freed

unsafe — the checks removed

method what it does
from_utf8_unchecked Take a Vec<u8> with no validation
as_mut_vec The underlying Vec<u8>, resizable — you owe valid UTF-8 back
from_raw_parts Rebuild from pointer, length and capacity — all three exact

Where to go next

Po polsku

To jest spis — po jednej stronie na każdą z 42 metod, które String udostępnia w stabilnym Ruscie 1.98. Podział między String a str warto sobie ułożyć raz i mieć go z głowy: String posiada tekst i potrafi go powiększyć, więc tutaj trafiło wszystko, co wymaga własności — budowanie, dopisywanie, usuwanie, oddanie bufora dalej. Całe czytanie (split, trim, find, replace, parse) mieszka po stronie str i działa na String bez żadnej konwersji, bo String dereferencjuje się do str — dlatego ta lista jest krótka, a tamta ma ponad osiemdziesiąt pozycji.

Trzy liczby, których mylenie jest największym źródłem kłopotów, mają dla polskiego czytelnika dodatkowy haczyk: len() zwraca bajty, a nie znaki, i nasze ogonki pokazują to natychmiast — "żółw" ma cztery znaki, ale siedem bajtów, bo ż, ó i ł zajmują w UTF-8 po dwa. Liczbę znaków daje dopiero chars().count(), i to przechodząc po całym łańcuchu znaków, więc nie jest to odczyt pola, tylko pętla. Trzecia liczba, capacity(), mówi wyłącznie, ile bufor może pomieścić, i nie jest częścią wartości: dwa łańcuchy o tym samym tekście i różnej pojemności są równe i tak samo się haszują — nigdy nie pisz testu, który sprawdza capacity().

Z tej samej różnicy bierze się druga pułapka. Metody przyjmujące przesunięcie w bajtachinsert, remove, truncate, drain, replace_range, split_off, extend_from_within — panikują na dwa sposoby: gdy przesunięcie wychodzi poza zakres oraz gdy wypada w środku znaku. Dla let mut s = String::from("żółw") wywołanie s.truncate(3) to właśnie ten drugi przypadek, bo trójka trafia między dwa bajty ó. Testem jest is_char_boundary, naprawą floor_char_boundary, które cofa przesunięcie do najbliższej poprawnej granicy — strona truncate pokazuje to na "héllo wörld" przy budżecie dwóch bajtów.

Szukaj po polsku: długość łańcucha w bajtach a w znakach · polskie znaki w UTF-8 · granica znaku w Ruscie · rust String len vs chars count · rust byte index is not a char boundary