</>
Skip to content
Rust lessons (19/43)

Rust — Strings

Two string types (yes, both matter)

TypeKindWhere it livesMutable?
Stringowned, growableheapyes
&str ("string slice")borrowed viewanywhere (heap/static)read-only
let owned = String::from("grow me");   // heap-allocated, editable
let slice  = "I am a &str";            // baked into the binary

Rule of thumb: use &str in function parameters and struct fields; use String when you must own or build.

Creating Strings

let s1 = String::from("hello");
let s2 = "hello".to_string();
let s3 = String::new();            // empty, grows later
let s4: &str = "slice";

All four hold valid UTF-8 text.

Building — push and format!

let mut s = String::from("Hello");
s.push_str(", world");     // append string
s.push('!');               // append single char

// idiomatic composition:
let full = format!("{} {}", "Ada", "Lovelace");

format! is usually cleanest — it takes {} placeholders like println! and returns an owned String without touching its inputs.

Concatenation quirks

let a = String::from("Hello, ");
let b = "world";

let c = a + b;             // moves `a`! a is gone now
println!("{}", c);         // "Hello, world"

let d = "Hello, ".to_string() + b;   // keep original by cloning/to_string first

+ uses addition that takes ownership of the left side. format! avoids all such surprises:

let out = format!("{}{}", "Hello, ", "world");

Indexing is forbidden — for good reason

let s = String::from("hello");
// s[0]              ❌ compile error!

Rust strings are UTF-8 byte arrays; s[0] would be ambiguous (byte? char?). Iterate or slice explicitly instead:

for ch in "héllo".chars() {
    println!("{}", ch);          // h é l l o — real characters
}

let hello = &s[0..5];            // SLICE — must land on char boundaries

.len() returns bytes, not letters: "héllo".len() is 6 (é is two bytes). Use .chars().count() for visible length.

Everyday methods

let s = String::from("  Hello, Rust  ");

s.trim();                    // "Hello, Rust"     strip whitespace
s.to_uppercase();            // case transforms → new String
s.contains("Rust");          // true
s.replace("Rust", "World");  // substituted copy
s.split(", ");               // iterator of pieces
s.starts_with("  Hello");    // prefix test

All return new values — originals untouched (immutability rules apply to Strings too unless mut).

Mini Practice

  1. Build a full name from first + last via format!.
  2. Prove "héllo".len() ≠ character count; fix with chars().count().
  3. Trim + lowercase user input before comparing to "yes".
  4. Split "a,b,c" and print each piece on its own line.
  5. Demonstrate a + b moving a; then rewrite with format!.

Next: ownership → (the big one)

Related Topics

Frequently Asked Questions about Strings

What is Strings in Rust?

Strings is a fundamental concept in Rust. This lesson explains it step by step with clear examples, making it easy for beginners to understand.

How do I learn Strings?

Start by reading the explanation above, then try the code examples. Practice by modifying the examples and experimenting with different values. Hands-on practice is the best way to learn Strings.

Why is Strings important in Rust?

Strings is essential for Rust development. Understanding this concept will help you write better code and solve real-world problems more effectively.