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:
- Use
.to_owned()orString::from(s)when promoting a borrowed string slice (&str) into an ownedString(emphasizing an ownership conversion from borrowed to owned). Notice that on a&strvariables, callings.clone()only copies the&strreference (&str), whereass.to_owned()allocates aString. - Use
.to_string()when converting a non-string value (such as a number42.to_string(), an IP address, or an error) into its human-readable text representation viaDisplay:
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)