139

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?

3
  • 1
    This error is not reproducible anymore in the current Rust (see in the Playground where I replaced ~"abc" with "abc".to_string() Commented Nov 11, 2015 at 13:40
  • 2
    I updated the example to use to_string() rather than the older '~' notation. However, it is interesting that this new version (your playground entry) now compiles and runs fine. Did a change in the == operator/fn happen so that it can compare &str and String types for value equality? Commented Sep 24, 2016 at 15:33
  • 2
    this all works just fine now see play.rust-lang.org/… Commented Oct 17, 2020 at 16:24

1 Answer 1

120

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

Sign up to request clarification or add additional context in comments.

4 Comments

The code now compiles on the latest version of Rust. What changed?
@OmarAbid I believe Rust is now more lenient about automatically taking references to values. The line c1 == s1 I think ends up compiled as &*c1 == s1, which is a &str == &str comparison. Note that c1 in the test_to_owned() example is still a String.
#[test] fn name() { let s1 = String::from("hello "); assert_eq!(s1, s1.clone()); assert_eq!(s1, s1.to_owned()); } this seems to be all the same
"For most types, clone() is sufficient because it's only defined on the underlying type and not on the reference type" — this is a bit backwards. Clone is implemented on all &T because immutable references are Copy. The difference is that ordinarily, deref coercion will attempt to turn (&T)::clone() into T::clone() and clone the pointed-to object. But when T: !Sized, like with str, there is no Clone implementation on T, so deref coercion fails and the only choice is to clone the &T.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.