How to learn ToOwned: ten steps, in order¶
Level: 101 → 201 · a learning path
One line: ToOwned itself adds one idea — a separate owned type — and the confusion lives in five ideas underneath it: unsized types, owned/borrowed pairs, Clone, method lookup and blanket impls. Re-reading the trait does not help; learning those five in order does, one page per step.
Each step is its own page: the idea, a checkpoint to predict, the verified answer from a program CI runs, and links out to the lesson that teaches it in full and to the official documentation. Predict each checkpoint before you open its answer. A wrong prediction means stay on that step: the next one assumes it.
Start at step 1 — or, if something specific surprised you, find its row below and start there. Step 1 states the question the path answers; steps 2 to 6 build what it takes to answer it; 7 to 9 apply it; 10 is optional, because most code never implements ToOwned at all.
Before step 1: the path assumes that a String owning a heap buffer, and a &str borrowing a view of one, is physical to you rather than abstract — you can draw both handles and the one buffer they share. If it is not yet, start in the library's Ownership section — Stack and heap and Ownership and moves are the two pages this path leans on — or with The Book's ch. 4.1, What Is Ownership? ↗. Three of its sections are this path's layer zero: The Stack and the Heap ↗; Memory and Allocation ↗, where a String is a pointer, a length and a capacity with its bytes on the heap, and a literal's text is hardcoded into the executable; and Variables and Data Interacting with Clone ↗, which step 4 picks up.
Find your step¶
Each surprise below is one missing idea, not a mystery about ToOwned. Start at the step in your row and work down from there.
| If this surprised you | Start at step |
|---|---|
ToOwned exists at all, when Clone already does |
1 |
size_of::<&str>() is not the size of a pointer |
2 |
name.clone() on a &str gave back a &str |
4 |
r.clone() on a &String gave a String, not a reference |
5 |
.clone() on a &&String gave a &String, but on a &mut String a String |
5 |
someone said .to_owned() on a &str works "by autoderef" |
5 |
Clone::clone(&r) gave a &String, but Clone::clone(r) and r.clone() gave a String |
5 |
a.to_owned() on a &Foo gave a &Foo, or E0308 said expected Foo, found &Foo |
6 |
42_i32.to_owned() compiles, and is 42 |
6 |
"hi".to_owned() is a String, but ToOwned::to_owned(&s) on a &str is a &str |
6 |
HashMap<String, _>::get accepts a &str |
7 |
a Cow diagram showed to_mut() and push as one step |
7 |
.to_owned() on an Rc did not copy the string |
8 |
.to_owned() on a Cow::Borrowed is still borrowed |
8 |
an article said Cow has into_borrowed, or that to_mut() gives a &mut str |
the Cow claims, run |
x.to_owned() inside a map over a &[&T] gave &Ts, or E0277 about FromIterator<&T> |
6 |
a kata had you write a to_owned that canonicalizes, and Cow::to_mut() changed your text |
three ToOwned katas, run |
clone_into into a String::new() saved nothing, but into an empty String::with_capacity(64) it did |
9 |
impl ToOwned for MyType is E0119 |
10 |
you cannot write Borrow for your view struct |
10 |
you are not sure whether to write clone, to_owned, to_string or into |
after the steps |
The steps¶
| Step | Page | What clicks | Checkpoint |
|---|---|---|---|
| 1 | ToOwned is Clone with a separate owned type |
type Owned, and the impl sitting on str, not on &str |
what "hi", [1, 2][..] and a Path turn into |
| 2 | Some types have no size | why you only ever hold a pointer to a str, and why it is two words |
is &str the size of &String? |
| 3 | Owned and borrowed are two different types | the borrowed half is the type behind the &, named by Deref |
the types of *s and *v |
| 4 | Clone hands back Self — and every &T is Clone |
why str cannot be Clone, and why every reference is |
is a usable after let b = a;? |
| 5 | The dot takes the first receiver that fits | the candidate list, and where a deref does — and does not — happen | three clones and three to_owneds |
| 6 | One blanket impl covers every Clone type |
impl<T: Clone> ToOwned for T, what it does on a &Foo, and why a &str has two impls |
42_i32.to_owned(), a &Ticket, and one &str two ways |
| 7 | Borrow is the way back |
type Owned: Borrow<Self>, HashMap::get, and how a Cow reads and writes |
seats.get("Ada"), and to_mut() twice |
| 8 | The traps are steps 5 and 6 together | Rc and Cow are Clone, so the wrapper is what gets copied |
a strong count and a Cow variant |
| 9 | clone_into refills instead of allocating |
reuse needs room already there | a roomy buffer's capacity |
| 10, optional | Implementing it — on the referent, or not at all | E0119, the unsized wrapper, and the inherent method |
two spellings of one call |
| after | Clone, ToOwned or From? |
the three side by side, with the heap read off the addresses | — |
Beside the steps:
ToOwnedin real code — fourteen excerpts from std at 1.98.0 and fromserde_json,bstr,camino,regex,borrowmeandbeef, each behind a pinned permalink; read it after step 10.- Lints around
ToOwned: bad and good, run — twenty-two rustc and clippy warnings, each with a program that triggers it, the warning, a silent version and when the lint is right, plus theto_ownedmistakes no lint catches. - Every
ToOwnederror, and its fix — twenty compiler errors aroundToOwned,Clone,BorrowandCow, findable by code or by message: the code, rustc's words, the mistake, and a fix that compiles. - Where
Clonewill not do: code only — ten pairs of fences,.clone()that fails to compile besideToOwnedthat works, each checked by the compiler on every build. - What
Cowexplanations get wrong, run — ten claims aboutCowfrom articles, books and chat answers, each checked against the compiler; read it after step 7. - Three
ToOwnedkatas, run — a circulating set of three katas with tests and solutions: which are sound, the one wrong word in the second, and the third's canonicalizingto_owned, which breaksCowand map lookups, repaired. - Helpful resources for the path — book chapters, the Reference, the Rustonomicon, articles and talks for every step, with the ones to read with care.
Everything else ToOwned touches¶
Explanations of ToOwned tend to arrive as a list of neighbouring topics. Each has a place on the path and a page in the library:
| Topic | On the path | In the library |
|---|---|---|
| ownership, moves, the stack and the heap | before step 1 | Ownership, Stack and heap, Ownership and moves |
dynamically sized types, ?Sized, wide pointers |
step 2 | str is unsized, Marker traits |
| owned types and their heap buffers | step 3 | String vs &str, Stack and heap |
Clone, Copy, and &T being Copy |
step 4 | Copy vs Clone |
method resolution, autoref and autoderef, Deref |
step 5 | Method resolution, Coercion |
| blanket impls and coherence | step 6 | Extension traits |
smart pointers: Rc, Arc, Box<str> |
step 8 | Rc, The third owned form |
Borrow (and BorrowMut, its &mut twin) |
step 7 | Borrow |
Cow, clone-on-write |
steps 7 and 8 | Cow |
| allocation cost and reuse | steps 8, 9 and after | clone_into, What a clone costs |
to_owned vs to_string vs From/Into |
after the steps | Making a string, From and Into |
| implementing it for your own type | step 10 | Implementing ToOwned |
| the std documentation page itself | step 1 | Reading the ToOwned docs |
Katas along the path¶
The steps say what to understand; these make you write it. Each lives on the page its step reads, in the order the steps unlock them.
| After step | Kata | On |
|---|---|---|
| 1 | Write the function that hands back the owned twin — one generic line, six arguments, and the ?Sized that lets str in |
Step 1 |
| 2 | Measure both halves of a reference — &str, &[i32], &dyn Display and &i32, and the ?Sized that takes all four |
str is unsized |
| 3 | One &str parameter, three callers — then flip it to String and count what each call site pays |
String vs &str |
| 4 | One E0382, three fixes — and what each costs the caller |
Copy vs Clone |
| 5 | Predict the receiver, then take it away from the dot — five calls, three that are not a String, each fixed two ways |
Step 5 |
| 6 | Give every slice a .middle() — one trait, two methods, and only one of them reachable through a dot |
Extension traits |
| 6 | Own every borrow in a &[&T] — the obvious closure, loud in one place and quiet in another, fixed three ways |
Step 6 |
| 6 | Predict the owned twin before you run it — six receivers, and the two everybody gets wrong | ToOwned |
| 7 | One lookup for every kind of key — three maps searched by their borrowed forms, zero allocations counted | Borrow |
| 7 | Pay only when you have to — a Cow that copies only the rows it changes |
Cow |
| 8 | Predict the count four times — one roster shared by three tallies, and the edge that leaks | Rc |
| 8 | Freeze a candidate column three ways — ending on the .to_owned() that clones a pointer instead of the text |
The third owned form |
| 9 | Four loops that all look like reuse — predict the allocations, then count them | clone_into |
| 10 | A slice that promises its order — Sorted<T> with Borrow, ToOwned and a Cow |
Implementing ToOwned |
| 10 | Repair the username kata — canonicalize at the door, copy in to_owned, and watch Cow and HashMap agree again |
Three ToOwned katas, run |
Every step has at least one. The full sequence, with every other kata in the library, is KATAS.md.
Tests for anything else you read¶
Most explanations of ToOwned get the trait right and one step underneath it wrong. The tell for each:
- "
&strimplementsToOwnedto produce aString."strdoes, with a hand-written impl. There is no hand-writtenimpl ToOwned for &str: the blanketimpl<T: Clone> ToOwned for Tcovers&strbecause&strisClone, and that impl produces a&str— step 1, then step 6. - "
.to_owned()works on a&strthrough autoderef" — or through autoref. Neither happens:str'sto_owned(&self)already takes a&str, so the receiver is passed as it is. Had a&been added or removed, the call would have returned a&str— step 5. - "On a reference,
clone()just copies the pointer." True only when the pointee is notClone— step 5. On a&Stringit allocates a newString. - "
ToOwnedalmost always allocates on the heap."42_i32.to_owned()touches no heap, and.to_owned()on anRcbumps a count. It allocates when the impl it lands on does — step 8 and after the steps. - "After
to_mut(), writing to aCownever allocates." The clone happens at most once, on the firstto_mut(). Growth is a separate cost: the freshStringhas no spare capacity, so the firstpushreallocates, as it would for anyString— step 7. - "
ToOwned'sOwnedform can be a transformed value, not just a clone." It compiles, and it breaks everything built on the trait:Cow::to_mut()rewrites the text before you write, and a map lookup by the borrowed form misses. Every std impl copies; put the transform in a constructor — threeToOwnedkatas, run. - "To implement
ToOwnedfor your struct, writetype Owned = Self." It compiles only while the struct is notClone, isCloneunder another name, and becomesE0119the day someone adds#[derive(Clone)]— step 10. - "
clone_intois another way to get owned data." It is the way to refill owned data. Into aStringwith no capacity it saves nothing; into one with enough capacity it reuses the buffer, even at length zero — step 9. - "Prefer
to_owned()toto_string(), because it is faster." Not since Rust 1.9; theToOwnedpage has the measurements. The argument that survives is thatto_ownednames what changes — the owner.
If you are coming from another language¶
- C++ — steps 2 and 3 are
std::stringandstd::string_view, except that Rust also names the thing the view points at. Step 4 is a copy constructor you have to call by name. Steps 5 and 6 are where C++ intuition misleads, and not because of spelling: in C++, copying the pointer (auto q = p;) and copying the pointee (auto s = *p;) look different, while Rust'sr.clone()is one spelling that the method search resolves to either, with nothing at the call site to say which. - Python — the trap has a Python twin.
b = agives the same list a second name, whileb = a[:]orcopy.copy(a)gives a new list, and the spelling tells you which you got. Rust'sr.clone()can be either, with the same spelling: on a&Stringit isa[:], a newString; on a&strit isb = a, the same text under a second reference (step 5). What has no Python twin is step 4: being copyable at all is a trait a type may decline to implement.
See also¶
- How to learn lifetimes — the same kind of page, for the other wall
ToOwned— the trait page steps 1, 6 and 7 keep returning to- Reading the
ToOwneddocs — the std page for the trait, block by block, with every claim on it run Borrow: look up an owned key with a borrowed one — step 7 in full, and the promise nothing checks- Implementing
ToOwnedfor your own type — step 10 in full - Traits — the section this path sits in
Po polsku¶
Jeśli ToOwned wciąż się „nie klei”, to prawie na pewno nie z powodu samej cechy (trait). Ona wnosi jedną nową informację — typ powiązany Owned, czyli osobny typ dla wersji posiadanej. Zamieszanie siedzi w pięciu pojęciach pod spodem: typy bez znanego rozmiaru (str, [T], Path), para własność–pożyczka jako dwa różne typy (String i str), Clone zwracające Self, wyszukiwanie metody po kropce i implementacja zbiorcza (blanket impl). Dlatego ponowne czytanie dokumentacji ToOwned nie pomaga, a przejście tych kroków po kolei — tak. Każdy krok ma teraz własną stronę.
Krok 1 zaczyna od sedna: implementacja jest na str, nie na &str — skoro Self to str, to &self to &str. Najważniejsze są potem kroki 4–6 razem. Każda referencja współdzielona &T jest Copy, więc i Clone, więc — przez impl<T: Clone> ToOwned for T — ma to_owned(). Kiedy Foo nie jest Clone, (&foo).to_owned() nie znajduje niczego na Foo i spada na implementację dla &Foo: dostajesz kopię wskaźnika, a z adnotacją typu — E0308 bez słowa o Clone. Na &String jest odwrotnie: String::clone pasuje pierwsze, więc dostajesz nowy String. Te same znaki, dwa wyniki, a rozstrzyga kolejność wyszukiwania — bez żadnej „autodereferencji” w przypadku &str.
Każdy krok ma punkt kontrolny: najpierw przewidź wynik, potem rozwiń zweryfikowany wydruk na stronie kroku. Błędna przepowiednia znaczy „zostań na tym kroku” — następny zakłada, że poprzedni już siedzi. Przy każdym kroku są linki do lekcji, do oficjalnej dokumentacji (Docs) i do słowniczka, a tabela Katas along the path podaje ćwiczenia w tej samej kolejności co kroki. Książki i rozdziały do każdego kroku zebrano na stronie z zasobami.
Szukaj po polsku: typy bez znanego rozmiaru · implementacja zbiorcza · rust to_owned vs clone · rust autoref method resolution · rust clone on reference returns reference