Reputation: 14604
In Rust, Clone
is a trait that specifies the clone
method (and clone_from
). Some traits, like StrSlice
and CloneableVector
specify a to_owned
fn. Why would an implementation need both? What is the difference?
I did an experiment with Rust strings, which have both methods, and it demonstrates that there is a difference, but I don't understand it:
fn main() {
test_clone();
test_to_owned();
}
// compiles and runs fine
fn test_clone() {
let s1: &'static str = "I am static";
let s2 = "I am boxed and owned".to_string();
let c1 = s1.clone();
let c2 = s2.clone();
println!("{:?}", c1);
println!("{:?}", c2);
println!("{:?}", c1 == s1); // prints true
println!("{:?}", c2 == s2); // prints true
}
fn test_to_owned() {
let s1: &'static str = "I am static";
let s2 = "I am boxed and owned".to_string();
let c1 = s1.to_owned();
let c2 = s2.to_owned();
println!("{:?}", c1);
println!("{:?}", c2);
println!("{:?}", c1 == s1); // compile-time error here (see below)
println!("{:?}", c2 == s2);
}
The compile time error for the to_owned
example is:
error: mismatched types: expected `~str` but found `&'static str`
(str storage differs: expected `~` but found `&'static `)
clone.rs:30 println!("{:?}", c1 == s1);
Why would the first example work but not the second?
Upvotes: 137
Views: 58804
Reputation: 185661
.clone()
returns its receiver. clone()
on a &str
returns a &str
. If you want a String
, you need a different method, which in this case is .to_owned()
.
For most types, clone()
is sufficient because it's only defined on the underlying type and not on the reference type. But for str
and [T]
, clone()
is implemented on the reference type (&str
and &[T]
), and therefore it has the wrong type. It's also implemented on the owned types (String
and Vec<T>
), and in that case clone()
will return another owned value.
Your first example works because c1
and s1
(and c2
and s2
) have the same types. Your second example fails because they don't (c1
is String
whereas s1
is &str
). That's a perfect example of why the separate methods are necessary.
As of current Rust, both now compile, but in test_clone()
c1
is a &str
and in test_to_owned()
it's a String
. I'm pretty sure it compiles as Rust is now more lenient about automatically referencing and dereferencing values. In this particular example I believe the c1 == s1
line is compiled as though it said &*c1 == s1
. If you wish to prove the types involved you can add a deliberate type error, such as let _: i32 = c1;
and the error message will show the type.
Upvotes: 119