Why use .to_string() when declaring a variable?

fn m(){

    let s1 = "hello".to_string(); // this
    println!("s1: {}", s1);

    let s2 = s1;                   

    println!("s1: {}", s1);    
    println!("s2: {}", s2);
}

Like if you prefix .to_string() then you also need to add & when assigning a value to a variable
But if without it, then & is optional. So why is all this necessary?

Because the tutorial you're reading is trying to explain why .to_string() make difference. For this simple example it's not necessary. But to write real world code in Rust you need to know what is ownership and borrowing and how to use them.

If you only need a &'static str literal (like "hello"), you might as well use it. But there will be times when you need a String specifically, because you need to modify it at run time, or another API demands it, etc.

pub fn hello(name: &str) -> String {
    "Hello, ".to_string() + name + "!"
}

fn main() {
    println!("{}", hello("world"));
    println!("{}", hello("Pat"));
}
1 Like

In Rust it's important to distinguish between owned strings, which are type String, and references to strings (or string parts), which are of type &str.

When you write "hello".to_string(), this is an owned string (i.e. String). These can be modified when in a mutable variable.

When you write "hello" you get a reference to a statically allocated (non-modifiable, non-growable) str. This reference has type &'static str. You can copy these references easily.

For duplicating a real String, however, you need to call .clone(), which imposes some runtime/memory overhead. But the advantage is that each String can be modified (or dynamically created).

fn main(){

    let s1 = "hello".to_string(); // this
    println!("s1: {}", s1);

    let s2 = s1.clone();                   

    println!("s1: {}", s1);    
    println!("s2: {}", s2);
}

(Playground)

Output:

s1: hello
s1: hello
s2: hello


(Informal) summary in simple terms:

  • String (created with .to_string()) can be dynamically generated, modified, grown but consumes memory and needs to be cloned for duplication.
  • &str (such when you write "some text" as a literal) refer to memory that's being owned/allocated elsewhere or to static memory. You cannot modify these &strs.

Here is another minimal example where type String is needed:

fn main(){
    let mut s = "hello".to_string();
    s.push_str(" world!");
    println!("s: {}", s);
    // this won't work:
    // let mut x = "hello";
    // x.push_str(" world!");
}

(Playground)

Output:

s: hello world!

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.