Strings Exercises

Overview

These exercises cover Rust’s two main string types: String (owned, growable) and &str (borrowed slice).


strings1 - Creating Owned Strings

Concept: The String type

fn current_favorite_color() -> String {
    // Two equivalent ways to create a String:
    "blue".to_string()
    // or: String::from("blue")
}
 
fn main() {
    let answer = current_favorite_color();
    println!("My current favorite color is {answer}");
}

Key Takeaways:

  • String is an owned, heap-allocated, growable string
  • &str (string slice) is a borrowed view into string data
  • Convert &strString with .to_string() or String::from()
TypeOwnershipMutabilityLocation
StringOwnedCan be mutHeap
&strBorrowedImmutableUsually stack/static

strings2 - String References and Coercion

Concept: Deref coercion from &String to &str

fn is_a_color_word(attempt: &str) -> bool {
    attempt == "green" || attempt == "blue" || attempt == "red"
}
 
fn main() {
    let word = String::from("green");
 
    if is_a_color_word(&word) {
        //             ^ &String → automatically coerced to &str
        println!("That is a color word I know!");
    } else {
        println!("That is not a color word I know.");
    }
}

Key Takeaways:

  • Functions often take &str to accept both &String and &str
  • &String is automatically coerced to &str (deref coercion)
  • This is why &str is preferred in function parameters — it’s more flexible

Coercion Flow:

String  →  &String  →  &str
        (borrow)    (deref coerce)

💡 Best Practice: Use &str in function parameters unless you need ownership.


strings3 - String Manipulation Methods

Concept: Common string operations — trim, format, replace

fn trim_me(input: &str) -> &str {
    input.trim()  // Removes leading and trailing whitespace
}
 
fn compose_me(input: &str) -> String {
    // format! is like println! but returns a String
    format!("{input} world!")
}
 
fn replace_me(input: &str) -> String {
    input.replace("cars", "balloons")  // Returns new String
}

Key Takeaways:

  • .trim() — removes leading/trailing whitespace, returns &str
  • format!() — like println! but returns a String instead of printing
  • .replace("old", "new") — replaces all occurrences, returns new String
MethodReturnsModifies Original?
.trim()&strNo (slice of original)
format!()StringN/A (creates new)
.replace()StringNo (creates new)

strings4 - String vs &str: Choosing the Right Type

Concept: Understanding when to use String vs &str

fn string_slice(arg: &str) { println!("{arg}"); }
fn string(arg: String) { println!("{arg}"); }
 
fn main() {
    // &str literals
    string_slice("blue");
 
    // Converting &str → String (multiple ways!)
    string("red".to_string());
    string(String::from("hi"));
    string("rust is fun!".to_owned());
    string("nice weather".into());  // Uses Into trait
    string(format!("Interpolation {}", "Station"));
 
    // Slicing a String → &str
    string_slice(&String::from("abc")[0..1]);  // ⚠️ Byte indexing!
 
    // Methods that return &str
    string_slice("  hello there ".trim());
 
    // Methods that return String
    string("Happy Monday!".replace("Mon", "Tues"));
    string("mY sHiFt KeY iS sTiCkY".to_lowercase());
}

Ways to convert &strString:

MethodExample
.to_string()"hello".to_string()
String::from()String::from("hello")
.to_owned()"hello".to_owned()
.into()"hello".into()
format!()format!("{}", "hello")

Methods that return &str:

  • .trim(), .trim_start(), .trim_end()
  • Slicing: &s[0..3] (⚠️ byte indices, not characters!)

Methods that return String:

  • .replace(), .to_lowercase(), .to_uppercase()

⚠️ Warning: String slicing &s[0..1] uses byte indices, not character indices! For Unicode-safe indexing, use .chars().nth(n).


Rust Book Reference