Skip to content

Rust Strings

Text handling is one of the first places Rust feels different from other languages, mostly because it actually has two string types. Once you know why, it stops being confusing.

  • &str (“string slice”) — text that already exists somewhere, usually written directly in your code. Fixed size, can’t grow.
  • String — an owned, growable piece of text. You can add to it, and it lives on the heap.
fn main() {
let literal: &str = "hello"; // a string slice
let owned: String = String::from("hello"); // an owned String
println!("{literal} / {owned}");
}
▶ Try it Yourself

A simple rule of thumb: if you’re just printing fixed text, "like this" (a &str) is fine. If you need to build, grow, or modify text at runtime, reach for String.

fn main() {
let mut greeting = String::from("Hello");
greeting.push_str(", World"); // add a &str
greeting.push('!'); // add a single char
println!("{greeting}");
}

Output:

Hello, World!
▶ Try it Yourself

You can join strings with +, or use format! (which is usually the clearer option):

fn main() {
let first = String::from("Hello, ");
let second = String::from("World!");
let combined = first + &second; // note the & before second
println!("{combined}");
// format! is often easier to read:
let name = "Ferris";
let greeting = format!("Hello, {name}!");
println!("{greeting}");
}
▶ Try it Yourself

A handful you’ll use constantly:

fn main() {
let text = " Hello, Rust! ";
println!("{}", text.trim()); // "Hello, Rust!" (no padding)
println!("{}", text.to_uppercase()); // " HELLO, RUST! "
println!("{}", text.to_lowercase()); // " hello, rust! "
println!("{}", text.contains("Rust")); // true
println!("{}", text.trim().len()); // 12 (length in bytes)
println!("{}", text.trim().replace("Rust", "World")); // "Hello, World!"
}
▶ Try it Yourself

To go character by character, use .chars():

fn main() {
for c in "abc".chars() {
println!("{c}");
}
}
▶ Try it Yourself
  • &str is borrowed, fixed text; String is owned and can grow.
  • Grow a String with .push_str() (for text) or .push() (for a single character).
  • Prefer format! over + when combining strings — it’s clearer and doesn’t move anything.
  • Common methods: .trim(), .to_uppercase(), .contains(), .replace(), .chars().

Quick check

1. Which string type is owned and can grow at runtime?

2. Why does this page prefer format! over + when combining more than one or two strings?

3. Why can't you index a Rust string like text[0]?

4. Which method adds a single character to a growing String, like greeting.push('!')?

Score: 0 / 4