Step 10: Implementing it — on the referent, or not at all¶
How to learn ToOwned › Step 10 of 10 · back: Step 9 — clone_into refills instead of allocating · next: After the steps — Clone, ToOwned or From?
Level: 201 → 301 · an optional last step: most code never implements ToOwned
One line: A type that is Clone cannot have an impl of its own (E0119). A struct holding references cannot be lent out of an owned struct, because borrow must return a &. The trait fits an unsized wrapper around one buffer; for everything else, write an inherent method.
Why impl ToOwned for MyType is E0119¶
If MyType is Clone, the blanket impl from step 6 already implements ToOwned for it, with Owned = MyType. A second impl is a conflict: "conflicting implementations of trait ToOwned for type V" (rustc 1.98.0). And the day someone adds #[derive(Clone)] to a type that had a hand-written impl, that impl stops compiling.
Why a view struct cannot be the borrowed half¶
Say you have NameRef<'a> { first: &'a str } and an owned Name { first: String }, and want NameRef: ToOwned<Owned = Name>. Step 7's bound then demands Name: Borrow<NameRef<'a>>:
That returns a reference to a NameRef, so a NameRef has to exist somewhere for it to point at — and a Name holds a String, not a NameRef. There is nothing to lend. str works because a String really does contain a str; your view struct is a new value, built on demand.
What fits: one buffer, wrapped as an unsized type¶
Sorted<T> is unsized like [T] (step 2), and #[repr(transparent)] makes it laid out exactly as [T] is, so a &[T] that has been checked can be cast into a &Sorted<T>. Its owned twin wraps a Vec<T>, which really does contain the [T] — so Borrow can lend it out, and ToOwned is legal. That is Implementing ToOwned in full.
What usually fits better: an inherent method¶
For a view struct, write fn to_owned(&self) -> Name directly on the type. The checkpoint is what that does to a call:
view.to_owned()— at the first matching candidate,&NameRef, both the inherent method and the blanket impl's method fit, and an inherent method wins over a trait method at the same candidate (step 5). You get aName.ToOwned::to_owned(&view)— naming the trait skips the inherent method, so you get the blanket impl and aNameRef.
Both spellings compile, which is the trap: generic code bounded by T: ToOwned always gets the second.
Checkpoint¶
Predict before you open the answer. NameRef<'a> derives Clone and has an inherent fn to_owned(&self) -> Name. What do view.to_owned() and ToOwned::to_owned(&view) return?
The answer
Verified output of implementing_it_or_not.rs — regenerated by tools/run_examples.py, never hand-typed.
Checkpoint. NameRef derives Clone and has an inherent to_owned. What comes back?
view.to_owned() -> Name { first: "Ada" } the inherent method
ToOwned::to_owned(&view) -> NameRef { first: "Ada" } the blanket impl
Only the inherent method's result outlives the text it was made from
kept.first = "Grace", its own buffer
Where the path goes from here¶
- After the steps:
Clone,ToOwnedorFrom? — the three traits side by side, and what each does to the heap. - The tests on the path page — the claims about
ToOwnedyou will keep meeting elsewhere, each with the step that settles it. - If a step did not stick, find your step again.
Go deeper¶
Read: Implementing ToOwned for your own type — put the trait on the referent, why the cast is sound, where it stops: one buffer and usually, skip the trait.
Around it in the library:
ToOwned— can you implement it yourself?- Extension traits — the rule that permits it, coherence from the other side
FromandInto— the orphan rule decides what you may write- A trait must be in scope — the case that forces the long spelling, an inherent method shadowing a trait's
- Lifetime annotations — on a struct it becomes part of the type, the
'aa view struct carries and its owned twin does not - Phantom types and a score is not a number, other wrappers that add a promise to a type
In real code: ToOwned in real code — std's own Path (§4) and serde_json's RawValue (§9) doing exactly this, and borrowme (§13) doing what std's traits cannot.
Docs: E0119 ↗ · Trait implementation coherence ↗ · The transparent representation ↗, which the unsized wrapper relies on
Words: orphan rule and Borrow<Q> in the glossary.
Kata: A slice that promises its order — Sorted<T> with Borrow, ToOwned and a Cow.
Po polsku¶
Własne impl ToOwned dla typu, który jest Clone, to E0119 — implementacja zbiorcza już go obejmuje. Struktura-widok z referencjami (NameRef<'a>) też odpada: Borrow musiałoby zwrócić &NameRef, a posiadany Name żadnego NameRef w sobie nie ma, więc nie ma na co wskazać.
ToOwned pasuje do typu bez rozmiaru, który opakowuje jeden bufor: #[repr(transparent)] struct Sorted<T>([T]); — jego posiadany bliźniak zawiera Vec<T>, a w nim naprawdę jest [T]. W pozostałych przypadkach lepsza jest zwykła metoda fn to_owned(&self) -> Name. Uwaga na pisownię wywołania: view.to_owned() trafia w metodę własną (wygrywa z metodą cechy), a ToOwned::to_owned(&view) — w implementację zbiorczą.
Szukaj po polsku: rust E0119 conflicting implementations · rust implement ToOwned custom type · rust repr(transparent) unsized wrapper
How to learn ToOwned › Step 10 of 10 · back: Step 9 · next: After the steps — Clone, ToOwned or From?