Strings and Text Processing

to_string() and to_owned()

Both .to_string() and .to_owned() convert a borrowed string slice (&str) into an owned String, but they come from different traits with different semantic purposes across the standard library.

Trait Origins and Comparison

Method / Function Defined By Accepted Receivers Purpose
s.to_owned() std::borrow::ToOwned Unsized borrowed types (str, [T], Path, CStr) Constructs the canonical owned counterpart (str -> String, [T] -> Vec<T>)
String::from(s) std::convert::From<&str> &str Explicit type conversion from &str to String
val.to_string() std::string::ToString (blanket impl for all T: Display) Any type implementing Display (i32, bool, &str, custom types) Formats a value into a String via its Display implementation
s.into() std::convert::Into<String> &str, Box<str>, Cow<'_, str> Type-inferred conversion when target type String is known from context

Performance on &str: Specialization Makes Them Identical

Historically, "hello".to_string() went through the std::fmt::Display formatting machinery and was slower than "hello".to_owned() or String::from("hello"). In modern stable Rust, the standard library specializes ToString::to_string for str and &str to delegate directly to String::from, so all three compile to the exact same memcpy allocation:

fn main() {
    let slice: &str = "rust-2024";

    // For a `&str`, all three execute the exact same machine code:
    let a: String = slice.to_owned();
    let b: String = String::from(slice);
    let c: String = slice.to_string();

    assert_eq!(a, b);
    assert_eq!(b, c);
    println!("All three equal: {a}");
}
All three equal: rust-2024

Style Conventions: When to Use Which

Even though .to_string(), .to_owned(), and String::from() have identical performance on &str, idiomatic Rust code distinguishes them by intent:

  1. Use .to_owned() or String::from(s) when promoting a borrowed string slice (&str) into an owned String (emphasizing an ownership conversion from borrowed to owned). Notice that on a &str variable s, calling s.clone() only copies the &str reference (&str), whereas s.to_owned() allocates a String.
  2. Use .to_string() when converting a non-string value (such as a number 42.to_string(), an IP address, or an error) into its human-readable text representation via Display:
use std::net::Ipv4Addr;

fn main() {
    // 1. Promoting a `&str` to an owned `String`:
    let host: String = "localhost".to_owned();

    // 2. Formatting a non-string Display type into a `String`:
    let port_str: String = 8080_u16.to_string();
    let ip_str: String = Ipv4Addr::new(127, 0, 0, 1).to_string();

    println!("{host} ({ip_str}:{port_str})");
}
localhost (127.0.0.1:8080)