Skip to content

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>>:

fn borrow(&self) -> &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

#[repr(transparent)]
struct Sorted<T>([T]);

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 a Name.
  • ToOwned::to_owned(&view) — naming the trait skips the inherent method, so you get the blanket impl and a NameRef.

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

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:

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?