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:
They do the same sh*t, Rust is badly designed!
If you want to skip to the concise conclusion you should go to the TLDR.
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:
|
|
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 ?
|
|
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:
|
|
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?
|
|
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.

