Background
In Rust, there are two main string types – &str (slice of a string, immutable, often a string literal) and String (dynamic, mutable string). Early in the language's development, the choice between them simplified working with efficient memory and ensured type safety when handling textual data due to a strict ownership and borrowing system.
The Problem
Many developers get confused when interacting between these types. For example, a string literal is &'static str, which is a reference to an immutable string allocated at compile time, while String can dynamically grow and contain data obtained at runtime. Questions arise about how to convert between types, correctly use ownership, and avoid unnecessary copying.
The Solution
Conversion between &str and String is straightforward if you understand the basic rules of ownership:
String via a reference (my_string.as_str()) or simple borrowing (&my_string).&str to String using to_string() or String::from().Code Example:
fn main() { let s_literal: &str = "hello"; let s_string: String = String::from(s_literal); let s_slice: &str = &s_string; let new_string = s_slice.to_string(); println!("{} {}", s_string, new_string); }
Key Features:
&str does not take up heap memory, always immutable.String dynamically allocates memory and can be modified.Can you modify a string literal in Rust?
No, a string literal (&'static str) is always immutable, any attempt to change a character will lead to a compilation error.
Is calling .to_string() on &str enough to get a mutable string without extra copies?
No, .to_string() always allocates new memory and copies the content. This is inevitable if a mutable string based on a slice is needed.
Can you obtain a &str reference from String without risking a lifetime leak?
Yes, the reference obtained this way (let s: &str = &my_string;) lives no longer than the original String. Attempting to return &str from a local String in a function will trigger a lifetime error.
&str reference to a temporary String that goes out of scope&str to String every time without necessity (unnecessary allocations)String::from("text") not to create a copy of dataFunction returns &str referring to a temporary String inside the function:
fn faulty() -> &str { let s = String::from("Oops"); &s // lifetime error! }
Pros:
Cons:
Function immediately returns String and transfers ownership to the caller:
fn correct() -> String { String::from("Safe!") }
Pros:
Cons: