Skip to content

The strings map

Level: reference · the map

One line: Text in Rust is one pattern — an owner and a view — met four times: in the two everyday types, in the memory they manage, in the characters they encode, and in the wider family for text that keeps different promises. This page is the door to every lesson about it, in the order the questions come up.

If you just want to know which type to write, read String vs &str — the tables below are the route, not the explanation.

Same rule as the other maps: order is presentation, so it lives here rather than in folder names — STRUCTS.md explains why in full, and OPTION.md and SHADOWING.md follow it too.


The territory, as a picture

flowchart LR
    S["text in Rust<br/>one pattern: an OWNER and a VIEW"]

    S --> TY["THE TWO TYPES<br/>String owns, &str looks"]
    S --> MEM["MEMORY<br/>what the owner is made of"]
    S --> ENC["ENCODING<br/>what the bytes mean"]
    S --> USE["MAKING & EDITING<br/>getting one, growing one"]
    S --> FAM["THE FAMILY<br/>three promises, one pattern"]

    TY --> T1["a literal is &'static str —<br/>in the binary, not the stack"]
    TY --> T2["deref coercion:<br/>&String becomes &str, free"]
    TY --> T3["parameters take &str,<br/>fields own String"]
    TY --> T4["String moves, &str copies"]
    TY --> T5["a slice is ptr + len;<br/>its indices are BYTES"]
    TY --> T6["&str and &'static str:<br/>on a literal, one type"]

    MEM --> M1["ptr / len / capacity on the stack,<br/>bytes on the heap"]
    MEM --> M2["growth doubles;<br/>with_capacity pre-pays"]
    MEM --> M3["a Vec of u8<br/>that promises UTF-8"]

    ENC --> E1["char: one Unicode scalar,<br/>4 bytes as a value"]
    ENC --> E2["in a String: 1-4 UTF-8 bytes,<br/>so len() counts bytes"]
    ENC --> E3["bytes, chars, UTF-16, graphemes —<br/>four answers to 'how long'"]
    ENC --> E4["walking it: u8, char, &str —<br/>splits are the gaps between matches"]

    USE --> U1["to_string comes free<br/>from Display"]
    USE --> U2["two &str will not add —<br/>+ needs an owned left"]
    USE --> U3["push_str grows in place;<br/>format! borrows everything"]
    USE --> U4["every edit is byte-indexed,<br/>so every edit can panic"]

    FAM --> F1["OsString / &OsStr —<br/>whatever the OS handed you"]
    FAM --> F2["CString / &CStr —<br/>text bound for C"]
    FAM --> F3["PathBuf / &Path —<br/>an OsString with path smarts"]

Start here

# Lesson Level The question it answers
1 String vs &str 101 → 201 Which of the two types do I write here — and why does every parameter want &str?
2 String slices 101 → 201 What a view actually is: the stale index it replaces, the E0502 that keeps it honest, and the one way &s[a..b] panics
3 The anatomy of a String 101 → 201 What the owner is: three words on the stack, bytes on the heap, and a capacity that is not the length
4 Making a String 101 → 201 to_string vs to_owned vs String::from vs into vs format! — and why Display is the only one you implement
5 Concatenating strings 101 → 201 How to join two pieces of text — and why + insists on an owned String on the left, which is the whole of E0369/E0308/E0368
6 Building a String 101 → 201 push_str, push, the + that consumes its left operand, and format! vs write! inside a loop
7 Meet the char 101 → 201 What the bytes encode — why .len() is not "how many characters", and why s[0] refuses to compile
8 Raw strings, escapes and the literal prefixes 101 → 201 Every way to write text in source — r"…", b"…", c"…", \x, \u{…} — and why "C:\temp\new" is two bytes shorter than it looks
9 Walking a String 101 → 201 Three item types and the split family — and why split(' ') and split_whitespace() disagree about empty fields
10 &'static str 201 Is it different from &str? On a literal, no — and the claim that a String can never yield one is false
11 String parameters worth copying 201 → 301 Which signature makes the caller pay — &str, impl AsRef<str>, impl Into<String> or a Cow return — counted in allocations rather than asserted
12 Six kinds of string 201 Why OsString and CString exist, and the one owned/borrowed pattern all six types repeat
13 str is unsized 201 Why you never hold a str, only a pointer to one — the size that belongs to the value, the fat pointer's second word, and ?Sized as a relaxation
14 Inside a Split 201 → 301 Why printing s.split(":") with {:?} gives a struct full of crit_pos and byteset instead of ["a", "b", "c"] — and what every field of it means
15 The third owned form 201 → 301 Box<str>, Rc<str>, Arc<str>: the capacity word dropped, and the interning move that makes a repeated column stop paying per row
16 Four lengths, and which one the other system means 201 One string, four counts — and why a name that fits a VARCHAR(12) can bounce off an nvarchar(12)
17 Searching without splitting 101 → 201 Is it there, where is it, does it start like this — and why one trait makes a char, a &str and a closure the same argument
18 Replacing part of a string 101 → 201 replace hands back a new String; replace_range and retain are the two that edit in place, and a chain of replaces is not a substitution table
19 str::as_str: the method that was stabilized and taken back 201 → 301 Why as_str works on a String and is E0658 on everything else that owns text — and what an inherent method added to str or [T] does to the crates that already extend them
20 Splitting on nothing 201 Why "abc".split("") yields ["", "a", "b", "c", ""] — and why the empty string is the one that splits into two pieces
21 Comparing and sorting text 201 What == and sort() actually compare — and why the answer is reproducible on every machine and still wrong for every reader
22 Parsing out of a string 101 → 201 Text on the way in — what names the type, what the string is allowed to look like, and what the failure will tell you
23 The format mini-language 201 Text on the way out — the whole grammar, and why a format spec is a request an impl is free to ignore
24 When the UTF-8 invariant broke 301 Has std's own string code ever been wrong about memory — and what did safe code have to do to break it?
25 Wrong, but not unsafe 201 → 301 And has it ever simply returned the wrong answer, with nothing unsafe anywhere near it?

Starting from nothing

The table above is ordered by the question you arrive with. If you arrive with none — new to Rust, or new to its text — this is the other order: by what each step needs from the one before. Ownership first, then the two types, then what they cost, then programs.

What the whole route builds, in four lines:

String   owned, growable, on the heap: a Vec<u8> that promises UTF-8
&str     borrowed and read-only: points into a String, the binary, or anything holding text
str      unsized: never held directly, only behind a pointer (&str, Box<str>, Rc<str>)
"text"   a literal is a &'static str, and its bytes are in the binary
Step Read What you can say afterwards
0 Ownership and moves · Borrowing · Stack and heap · Copy vs Clone Who frees a value, and why a String moves where a &str copies
1 String vs &str A parameter takes &str, and a &String coerces to it for free because String: Deref<Target = str>
2 The anatomy of a String · &'static str A String is pointer, length and capacity with its bytes on the heap; a literal's bytes are in the binary
3 String slices · str is unsized A &str is a fat pointer — address and length — into bytes someone else owns, and &s[a..b] panics off a character boundary
4 Making a String · Concatenating strings · Building a String The ways from &str to String, and why + needs an owned left
5 Meet the char · Four lengths A character is 1 to 4 bytes in UTF-8, so .len() counts bytes
6 Cow: borrow until somebody writes · String::from_utf8_lossy A function that allocates only sometimes, and std's own example of one
7 str methods · String methods The reference, once you know what you are looking for
8 String parameters worth copying Which signature to write — &str in, String out, a String field — with the allocations counted

Every step but 7, the reference you look things up in, has katas to type; KATAS.md groups them by subject.

The lessons strings lean on

Strings are the worked example half the library's ownership pages already use, so the deep explanations live there:

Lesson Level What it settles for strings
Ownership and moves 101 The E0382 a moved String produces — in full, with a value that announces its own death
Copy vs Clone 101 → 201 Why &str copies freely, and one String field makes a whole struct move
Borrowing 101 → 201 The rule that refuses a view held across a push_str
How to learn lifetimes 201 Why "own String, clone when stuck" is legitimate advice while &str fields wait
Meet the byte 101 → 201 The unit len counts in — this map's encoding arc is what those bytes mean
What is a record, in memory? 201 String fields chosen inside a real struct design
Path and PathBuf 201 The family's honorary pair, in full — a stub for now
A file is bytes; a String is a promise 201 Where the promise is dropped and where it is checked — write_all takes &[u8], read_to_string refuses non-UTF-8 with InvalidData and leaves your String untouched, and include_str! runs the same check at compile time
Cow: borrow until somebody writes 201 The maybe-owned string: borrow when the text needs no change, allocate only on the write
The global allocator 301 Where the bytes on the heap come from — and how to count a String's allocations instead of inferring them from capacity()

Still written as outlines

Named honestly, because a map that only lists what exists is a map of the wrong territory. Each of these is a stub — a real page at a real URL, carrying the questions the finished lesson has to answer and the links to its neighbours, but with no runnable example behind it yet. That is the boundary this library draws: a stub states what it does not know, and nothing on it has been through the check that backs every other claim here.

The page Level What it will answer
When String is too slow 301 with_capacity in anger, smallstr / smartstring, and avoiding a format! that a literal would do

A stub graduates by gaining an examples/ program, losing its notice, and — if it earned one on the way — a row in KATAS.md. If you want one of these next, that is the table to point at.

Every method, one page each

The lessons above are the ideas; this is the reference to reach for once you know what you are looking for.

reference pages covers
str methods 83 Reading text — searching, splitting, trimming, case, parsing, escapes, and the two byte-offset panics
String methods 42 Owning text — building, growing, removing, capacity, and giving the allocation away

Each page states the signature and the stability, explains what the method does and the mistake it is usually part of, and ends with a complete program plus its verified output. String derefs to str, so every method on the first list works on a String too.

Where else to learn this

Strings: links, books and videos collects the outside sources — the Book's ch. 8.2, Programming Rust ch. 17, Easy Rust ch. 14 and its video, the essays, the two forum threads worth reading whole — including String vs str, why?, the source of the StringBuf reading on the String vs &str page — and the external exercise sets (rustlings' strings and conversions) that map onto these lessons.

The string crates is the crates half: fifteen crates for the jobs std leaves out — graphemes, normalization, legacy encodings, bytes that are not UTF-8, short strings off the heap — each beside the lesson here that covers what std does instead.

Looking a term up

GLOSSARY.md defines the vocabulary these pages use — string slice, string literal, deref coercion, capacity, Unicode scalar value, grapheme cluster, the OsString and CString pairs — and every entry links the page that explains it properly.

Po polsku

Tekst w Ruscie to jeden wzorzec spotykany cztery razy: właściciel i widok. String posiada, &str ogląda — i ta sama para wraca przy Vec<T> / &[T], przy PathBuf / Path, przy OsString / OsStr. Kto zobaczy ten wzorzec raz, przestaje uczyć się każdej z tych par osobno.

Dla polskiego czytelnika ten dział jest ważniejszy niż dla angielskiego, i to z jednego konkretnego powodu: każda z liter ą ć ę ł ń ó ś ź ż zajmuje w UTF-8 dwa bajty. Dlatego "Łódź".len() daje 7, a nie 4; dlatego &s[0..1] potrafi wywołać panikę o granicy znaku; i dlatego cała rodzina metod z ascii w nazwie po cichu nic nie robi na polskim tekście — to_ascii_uppercase nie ruszy ł, a split_ascii_whitespace nie widzi twardej spacji, którą polska typografia stawia po spójnikach jednoliterowych. Angielski czytelnik może przejść przez ten dział i nigdy się na to nie natknąć; polski natknie się pierwszego dnia.

Nazewnictwo, którego trzymamy się w całej bibliotece: String to łańcuch znaków, &str to wycinek łańcucha, a char to znak — przy czym jest to punkt kodowy Unicode, a nie bajt i nie „litera na ekranie".

Szukaj po polsku: łańcuchy znaków w Ruscie · String a &str · kodowanie UTF-8 · polskie znaki diakrytyczne · rust char boundary panic