Skip to content

Where Clone will not do: code only

How to learn ToOwned › Beside the steps · the question it answers: Step 1

Level: 101 → 201 · code, not prose

One line: Ten places where .clone() does not compile and ToOwned does. In each pair, the first fence must fail and the second must compile, and check_fences.py holds both to that on every build. The errors in the comments are rustc 1.98.0's.

1. A &str into a String

pub fn keep(name: &str) -> String {
    name.clone() // E0308: expected `String`, found `&str`
}
pub fn keep(name: &str) -> String {
    name.to_owned()
}

2. Dereferencing first does not help

pub fn keep(name: &str) -> String {
    (*name).clone() // E0599: no method named `clone` found for type `str`
}
pub fn keep(name: &str) -> String {
    (*name).to_owned()
}

3. A slice into a Vec

pub fn keep(scores: &[i32]) -> Vec<i32> {
    scores.clone() // E0308: expected `Vec<i32>`, found `&[i32]`
}
pub fn keep(scores: &[i32]) -> Vec<i32> {
    scores.to_owned()
}

4. A &Path into a PathBuf

use std::path::{Path, PathBuf};

pub fn keep(file: &Path) -> PathBuf {
    file.clone() // E0308: expected `PathBuf`, found `&Path`
}
use std::path::{Path, PathBuf};

pub fn keep(file: &Path) -> PathBuf {
    file.to_owned()
}

5. Refilling a String you already have

pub fn refill(buf: &mut String, name: &str) {
    buf.clone_from(name); // E0308: expected `&String`, found `&str`
}
pub fn refill(buf: &mut String, name: &str) {
    name.clone_into(buf);
}

6. One function for every borrowed type

pub fn own_all<T: Clone>(items: &[&T]) -> Vec<T> {
    items.iter().map(|&item| item.clone()).collect()
}

pub fn names() -> Vec<String> {
    own_all::<str>(&["Ada", "Grace"]) // E0277: the trait bound `str: Clone` is not satisfied
}
pub fn own_all<B: ToOwned + ?Sized>(items: &[&B]) -> Vec<B::Owned> {
    items.iter().map(|&item| item.to_owned()).collect()
}

pub fn names() -> Vec<String> {
    own_all::<str>(&["Ada", "Grace"])
}

pub fn rows() -> Vec<Vec<i32>> {
    own_all::<[i32]>(&[&[1, 2][..], &[3][..]])
}

7. A struct that keeps the owned form of whatever it is lent

pub struct Keeper<T: Clone> {
    pub kept: T,
}

impl<T: Clone> Keeper<T> {
    pub fn new(borrowed: &T) -> Self {
        Keeper { kept: borrowed.clone() }
    }
}

pub fn keep_name() -> Keeper<str> { // E0277: the trait bound `str: Clone` is not satisfied
    Keeper::new("Ada")
}
pub struct Keeper<B: ToOwned + ?Sized> {
    pub kept: B::Owned,
}

impl<B: ToOwned + ?Sized> Keeper<B> {
    pub fn new(borrowed: &B) -> Self {
        Keeper { kept: borrowed.to_owned() }
    }
}

pub fn keep_name() -> Keeper<str> {
    Keeper::new("Ada")
}

pub fn keep_bytes() -> Keeper<[u8]> {
    Keeper::new(b"Ada")
}

8. Clone-on-write, built on Clone

pub enum MyCow<'a, T: Clone> {
    Borrowed(&'a T),
    Owned(T),
}

pub fn underscores(label: &str) -> MyCow<'_, str> { // E0277: the trait bound `str: Clone` is not satisfied
    if label.contains(' ') {
        MyCow::Owned(label.replace(' ', "_"))
    } else {
        MyCow::Borrowed(label)
    }
}
use std::borrow::Cow;

pub fn underscores(label: &str) -> Cow<'_, str> {
    if label.contains(' ') {
        Cow::Owned(label.replace(' ', "_"))
    } else {
        Cow::Borrowed(label)
    }
}

9. Count words, allocating only for a word not seen before

use std::collections::HashMap;

pub fn count(tally: &mut HashMap<String, u32>, word: &str) {
    match tally.get_mut(word) {
        Some(n) => *n += 1,
        None => {
            tally.insert(word.clone(), 1); // E0308: expected `String`, found `&str`
        }
    }
}
use std::borrow::Borrow;
use std::collections::HashMap;
use std::hash::Hash;

pub fn count<K, Q>(tally: &mut HashMap<K, u32>, word: &Q)
where
    K: Borrow<Q> + Hash + Eq,
    Q: ToOwned<Owned = K> + Hash + Eq + ?Sized,
{
    match tally.get_mut(word) {
        Some(n) => *n += 1,
        None => {
            tally.insert(word.to_owned(), 1);
        }
    }
}

10. Owning a list of words — and the & that still gets in the way

pub fn own_words(words: &[&str]) -> Vec<String> {
    words.iter().cloned().collect() // E0277: `Vec<String>` cannot be built from an iterator over elements of type `&str`
}
pub fn own_words(words: &[&str]) -> Vec<String> {
    words.iter().map(|word| word.to_owned()).collect() // E0277: the same — `word` is a `&&str`, so this is `&str`'s to_owned
}
pub fn own_words(words: &[&str]) -> Vec<String> {
    words.iter().map(|&word| word.to_owned()).collect()
}

All of them, run

Verified output of where_clone_will_not_do.rs — regenerated by tools/run_examples.py, never hand-typed.

 1. keep("Ada")                -> "Ada", alloc::string::String
 2. (*name).to_owned()         -> "Ada", alloc::string::String
 3. keep(&[3, 1, 2])           -> [3, 1, 2], alloc::vec::Vec<i32>
 4. keep(Path::new(..))        -> "notes.txt", std::path::PathBuf
 5. refill(&mut buf, "Grace")  -> "Grace", same buffer: true
 6. own_all::<str>             -> ["Ada", "Grace"], alloc::vec::Vec<alloc::string::String>
    own_all::<[i32]>           -> [[1, 2], [3]], alloc::vec::Vec<alloc::vec::Vec<i32>>
 7. Keeper::<str>::new         -> kept "Ada", alloc::string::String
    Keeper::<[u8]>::new        -> kept [65, 100, 97], alloc::vec::Vec<u8>
 8. underscores("first name")  -> Owned "first_name"
 8. underscores("surname")     -> Borrowed "surname"
 9. count words, &str keys     -> [("ada", 2), ("grace", 1)]
    count chunks, &[u8] keys   -> [([71, 69, 84], 2), ([80, 85, 84], 1)]
10. own_words(&["Ada", ..])    -> ["Ada", "Grace"], alloc::vec::Vec<alloc::string::String>

Why each one fails

§ The step that explains it
1, 3, 4 Step 4 — clone returns Self, and on a &str Self is &str
2, 6, 7, 8 Step 2 and step 4 — str has no size, so it can never be Clone
5 Step 9 — clone_from wants the same type; clone_into wants the owned twin
8 Step 7 — Cow is built on ToOwned for exactly this reason
9 Step 7 — Borrow for the lookup, ToOwned for the insert
10 Step 5 and step 6 — on a &&str, .to_owned() finds &str's impl first

Po polsku

Dziesięć par kodu: w każdej pierwsza wersja z .clone() się nie kompiluje, a druga z ToOwned — tak. Obie są sprawdzane przy każdym budowaniu biblioteki (compile_fail musi się nie skompilować). Komentarze w kodzie to komunikaty rustc 1.98.0, a sekcja „All of them, run” pokazuje zweryfikowany wynik działających wersji. Tabela na końcu wskazuje krok ścieżki, który wyjaśnia każdą porażkę — w sekcji 10 pułapką jest nawet samo to_owned(), wołane na &&str.

Szukaj po polsku: rust clone vs to_owned · rust str Clone not satisfied · rust ToOwned generic ?Sized