Rust's Two String Types: String vs. &str
Think of `String` as an owned, growable text buffer on the heap, while `&str` is a borrowed, fixed-size view into string data. This distinction is key to Rust's memory safety. Functions often take `&str` to flexibly accept both types.
WHY IT EXISTS Rust exposes memory management for strings explicitly to enforce safety and prevent common bugs found in other languages. Instead of hiding the difference between stack/static data and heap-allocated data, Rust makes the ownership of string data clear through its type system with String and &str.
THE MENTAL MODEL Think of String as owning a book. You can write in it, tear out pages, and change its contents. It's yours to manage and destroy. An &str is like a library card that gives you temporary, read-only access to a book. You can read it, but you can't alter the library's copy, and you must eventually return it.
HOW IT WORKS String is a struct that owns its data. It's stored on the heap, which means it can grow or shrink at runtime. It consists of a pointer to the data, a length, and a capacity. When a String goes out of scope, its memory is automatically freed.
&str, or a string slice, is a borrowed reference. It's an immutable view into a string's data, which could be owned by a String, or exist in the program's binary (like a string literal). It's a 'fat pointer' containing a memory address and a length, but it does not own the underlying data.
WHEN TO USE IT Use String when you need to own and mutate string data. This is common when you're creating a string at runtime, such as from user input or by concatenating other strings. A String is necessary when the data must outlive the scope where it was created.
Use &str as function parameters whenever you only need to read string data. This is more flexible because it allows the function to accept owned Strings (by passing a slice like &my_string), string literals ("hello"), and other string slices.
WHEN NOT TO USE IT Avoid using String for function arguments if the function doesn't need to take ownership or modify the string; use &str instead to prevent unnecessary memory allocations and copies. Also, do not try to index into a String or &str with an integer (e.g., s[i]). Rust's strings are UTF-8, so a character can be more than one byte, making integer indexing an invalid operation that Rust prevents at compile time.
ONE CANONICAL EXAMPLE A function that accepts a &str is more versatile.
fn print_message(message: &str) { println!("{}", message); }
This function can be called with a string literal: print_message("Hello from a literal!");
It can also be called with a reference to an owned String: let my_string = String::from("Hello from a String!"); print_message(&my_string);
If the function took message: String, you could not pass a literal directly, and passing my_string would move ownership into the function.
Read the original → doc.rust-lang.org
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.