Difference Between clone and to_owned methods in Rust

Sep 20, 2021 • 4 minutes to read

In this blog post I will try to give you some clues on a question that a lot of people are asking me when talking about Rust:

The f*ck is the difference between clone and to_owned methods?
They do the same sh*t, Rust is badly designed!

Ok

If you want to skip to the concise conclusion you should go to the TLDR.

I will assume that you already know how Rust works especially the ownership concept.
If not you’ll surely miss a lot of important stuff in this article.
If you want to read about the ownership in Rust, check the Rust book

So let’s start!

I’ll try to expose why those methods attempt to do the same at first sight but can be used in different use cases where they act differently.

But first let’s see what Copying and Cloning mean. Those two terms represent the fact that we want to duplicate some data in some way. But we will see that some data can be simply copied and others need some more actions. To drive how the compiler should act we have access to two Trait which are Copy and Clone. Each one represents a different behaviour.

In Rust Copy and Clone are traits which can be implemented by numerous types, it allows duplication/copy of objects/ref.

As defined in the Rust doc, Copy trait is a marker (the full path of Copy is std::marker::Copy) and defines that the type’s values can be duplicated simply by copying bits. As we can imagine not every type can implement Copy, the best example is String which implement Clone but not Copy.

String in detail

String is defined by three components internally:

  • A pointer to some bytes
  • A length
  • A capacity

(in fact all of those three components are a single one, a Vec<u8>)
Because String owns a pointer, we can’t use Copy trait. It would end up copying the pointer which would lead to a double free at some point. That’s why String can’t implement Copy.

Following this rule, &mut T can’t be Copy even if T: Copy because &mut is a mutable reference.

fn implement_copy<T: Copy>() {}

fn main() {
    implement_copy::<&u8>(); // Ok
    implement_copy::<&mut u8>();
    ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `&mut u8`
}

Another interesting thing is that every type that implements Copy must implement Clone. In fact, when a type implements Copy the Clone implementation is just returning *self. This is because Clone is a supertrait of Copy.

But let’s see Clone. As defined in the Rust documentation, Clone is explicit, meaning that you need to explicitly ask for it. The compiler will not do it for you. Clone exposes two methods clone and clone_from.

Another interesting thing to know about Clone is that it returns its caller. Let’s see what it means:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
fn main() {
    let s1: &'static str = "hello";
    let s2 = "hello world".to_string();

    let c1: &'static str = s1.clone();
    let c2: String = s2.clone();

    assert_eq!(type_id(c1), type_id(s1)); // TypeIds are matching
    assert_eq!(type_id(c2), type_id(s2)); // TypeIds are matching
}

// Returns the TypeId of the input type.
fn type_id<T: 'static>(_: T) -> std::any::TypeId {
    std::any::TypeId::of::<T>()
}

We can see that the TypeIds are the same in both assertions meaning that we cloned the same type. Cloning &'static str returns a &'static str and a cloning a String returns a String. Perfect.

But what happens when we change clone to to_owned ?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
fn main() {
    let s1: &'static str = "hello";
    let s2 = "hello world".to_string();

    let c1 = s1.to_owned();
    let c2 = s2.to_owned();

    assert_eq!(type_id(c1), type_id(s1));
    assert_eq!(type_id(c2), type_id(s2));
}

thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `TypeId { t: 201574135764950547 }`,
 right: `TypeId { t: 9147559743429524724 }`', src/main.rs:8:5

As we can see the TypeIds are not matching anymore for c1, meaning that c1 is not a &'static str.

Let’s add some type to check which type it is:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
fn main() {
    let s1: &'static str = "hello";
    let s2 = "hello world".to_string();

    let c1: &'static str = s1.to_owned();
    let c2 = s2.to_owned();

    assert_eq!(type_id(c1), type_id(s1));
    assert_eq!(type_id(c2), type_id(s2));
}

error[E0308]: mismatched types
 --> src/main.rs:5:32
  |
5 |         let c1: &'static str = s1.to_owned();
  |                 ------------   ^^^^^^^^^^^^^
  |                 |              |
  |                 |              expected `&str`, found struct `String`
  |                 |              help: consider borrowing here: `&s1.to_owned()`
  |                 expected due to this

All right! The compiler is telling us that it found a String where we asked for a &str.

ToOwned returns the Owned version of a type. But what happens if we ask for a String when calling clone on a &'static str?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
fn main() {
    let s1: &'static str = "hello";
    let s2 = "hello world".to_string();

    let c1: String = s1.clone();
    let c2 = s2.to_owned();

    assert_eq!(type_id(c1), type_id(s1));
    assert_eq!(type_id(c2), type_id(s2));
}

error[E0308]: mismatched types
 --> src/main.rs:5:22
  |
5 |     let c1: String = s1.clone();
  |             ------   ^^^^^^^^^^
  |             |        |
  |             |        expected struct `String`, found `&str`
  |             |        help: try using a conversion method: `s1.to_string()`
  |             expected due to this

It is still not compiling because of mismatched types.

TLDR

So, to summarize, Clone and ToOwned are acting the same on type T but they act differently on &T.

Clone returns its caller meaning that calling clone on a &T will give us a &T (Clone is not implemented for &mut T obviously).

ToOwned, on the other hand, will return the owned type of the caller meaning that calling to_owned on &T will give us a T.
ToOwned is also possible on mutable references &mut T it will obviously give us a T.

Another important point is that any type that implements Clone also implements ToOwned.

rust

Building Chekov - Part 1: Design the EventStore

comments powered by Disqus