You create strings in Rust using either string literals (immutable &str slices) for compile-time constants or the String type for owned, mutable data. Choose &str for fixed text like keys or messages, and String when you need to modify content or own the data on the heap.
For immutable, compile-time known text, use string literals. These are zero-cost and stored directly in the binary. They are actually &'static str slices pointing to read-only memory.
fn main() {
// Immutable string slice (borrowed)
let greeting: &str = "Hello, world!";
// No allocation, cannot be modified
// greeting.push_str(" again"); // ERROR: cannot modify
}
For dynamic, owned, and mutable text, use the String type. This allocates memory on the heap and grows as needed. You can create one from a literal using the to_string() method or the String::from() constructor.
fn main() {
// Owned, mutable string
let mut s = String::from("Hello");
// Modify the content
s.push_str(", world!");
s.push('!');
println!("{}", s); // Output: Hello, world!!
}
When passing strings to functions, prefer &str for parameters to avoid unnecessary allocations and ownership transfers. This allows you to pass both string literals and String values seamlessly because String implements Deref to str.
fn print_greeting(msg: &str) {
println!("{}", msg);
}
fn main() {
let owned = String::from("Dynamic");
let borrowed = "Static";
// Both work because String derefs to &str
print_greeting(&owned);
print_greeting(borrowed);
}
Key distinction: &str is a view into a string (borrowed), while String owns the data. If you need to modify the text or store it in a collection that outlives the current scope, use String. If you just need to read fixed text, use &str. Converting between them is cheap: &String automatically coerces to &str, and String::from(&str) creates a new owned copy.