Rust Programming
String vs Str
Coding Differences
Programming Languages
Rust String Types

What are the differences between Rust's `String` and `str`?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Rust has two closely related string concepts that beginners often mix up: String and str. The short version is that String owns growable UTF-8 text on the heap, while str is the unsized string slice type that you usually use through &str.

String Owns the Data

String is an owned, heap-allocated, growable string type.

rust
1fn main() {
2    let mut name = String::from("Ada");
3    name.push_str(" Lovelace");
4    println!("{name}");
5}

Important properties of String:

  • it owns its bytes
  • it can grow and shrink
  • it is allocated on the heap
  • you can mutate it if the binding is mutable

This makes String the right choice when your code needs to build or modify text dynamically.

str Is the String Slice Type

str is not usually used by itself. It is an unsized type, so you almost always encounter it as a reference: &str.

rust
1fn greet(name: &str) {
2    println!("Hello, {name}");
3}
4
5fn main() {
6    greet("Rust");
7}

&str is a borrowed view into UTF-8 text that lives somewhere else. That text might come from:

  • a string literal
  • part of a String
  • another borrowed string slice

The key point is that &str does not own the text. It only borrows it.

Why &str Is So Common

Rust APIs often prefer &str as input because it is flexible. A function that accepts &str can work with:

  • string literals
  • borrowed String values
  • slices of existing strings

For example:

rust
1fn print_label(text: &str) {
2    println!("{text}");
3}
4
5fn main() {
6    let owned = String::from("owned string");
7    let literal = "string literal";
8
9    print_label(&owned);
10    print_label(literal);
11}

That is why &str is often the best parameter type when your function only needs to read text.

Converting Between Them

You can easily move between String and &str.

From String to &str:

rust
1fn main() {
2    let text = String::from("hello");
3    let slice: &str = text.as_str();
4    println!("{slice}");
5}

From &str to String:

rust
1fn main() {
2    let slice = "hello";
3    let owned = slice.to_string();
4    println!("{owned}");
5}

This is a normal part of Rust programming. The important thing is to choose ownership only when you really need it.

Mutability and Growth

One major practical difference is growth.

You can append to a String:

rust
1fn main() {
2    let mut text = String::from("foo");
3    text.push('!');
4    println!("{text}");
5}

But you cannot append to &str, because a string slice is just a borrowed view into existing text. It has no authority to resize the underlying storage.

That means:

  • use String for building or modifying text
  • use &str for reading or borrowing text

String Literals Are &'static str

Rust string literals are not String. They are &'static str.

rust
1fn main() {
2    let text = "hello";
3    println!("{text}");
4}

That means the literal is a string slice pointing to statically stored program data. It is not heap-allocated and not growable.

This is one of the reasons beginners get confused. They write text in quotes and assume they created a String, but they actually created a string slice.

Performance and API Design

From an API design perspective, accepting &str is often better than accepting String because it avoids forcing callers to allocate when allocation is unnecessary.

Badly constrained:

rust
fn print_name(name: String) {
    println!("{name}");
}

Better for read-only input:

rust
fn print_name(name: &str) {
    println!("{name}");
}

The second version is more ergonomic and more flexible because callers can pass either borrowed or owned text.

Common Pitfalls

The most common pitfall is thinking str and String differ only by syntax. They differ by ownership, mutability, and memory model.

Another mistake is writing functions that require String even though they only need read-only text. That creates unnecessary allocations and less flexible APIs.

A third issue is forgetting that string literals are &'static str, not String.

Finally, beginners often try to index strings like arrays of characters. In Rust, strings are UTF-8, so character boundaries and byte boundaries are not always the same thing.

Summary

  • 'String is an owned, heap-allocated, growable UTF-8 string type.'
  • 'str is the string slice type, usually used as &str.'
  • Use String when you need ownership or mutation.
  • Use &str when you only need to borrow or read text.
  • Good Rust APIs often accept &str because it is more flexible than requiring String.

Course illustration
Course illustration

All Rights Reserved.