Thuta Learning
Rust
ExercisesProgrammingbeginner

Exercise — Fix Borrow Checker Errors

What you'll walk away with

  • Explain the core ideas behind Exercise — Fix Borrow Checker Errors
  • Run the sample Rust code and verify its output
  • Apply the technique correctly to the Tutorial Platform and production scenarios

Build the mental model

The error patterns encountered in Lesson 6 (ownership move), Lesson 7 (the borrow rule: one mutable OR many immutable), and Lesson 15 (lifetimes) are not meant to be recited as theory here — this exercise practices the debugging skill of spotting and fixing those symptoms yourself in real broken code. Rust's compiler error messages tend to be more helpful than in most languages — "cannot borrow as mutable because it is also borrowed as immutable" (a Lesson 7 rule violation), "value borrowed here after move" (reusing a value after Lesson 6's move), "missing lifetime specifier" (a Lesson 15 lifetime annotation is needed) — each clearly points at which rule was broken, so carefully reading the error message should be the first step in finding a fix. Most fixes come down to choosing one of three patterns: (1) replace an ownership move with a borrow (`&`), (2) call `.clone()` for an explicit deep copy, or (3) shrink a scope so conflicting borrows no longer overlap — each fix has its own tradeoff (performance cost versus code complexity) worth understanding. Rather than seeing borrow-checker errors as an annoyance, it helps to reframe them as catching, at compile time, exactly the kind of bug that would otherwise surface as a production runtime crash.

Connect it to a real scenario

Take three real-world scenarios from the content analyzer codebase: (1) code calling `fn print_and_return(s: String) -> String { println!("{s}"); s }` and then trying to reuse the original variable — the move error is fixed by switching to a `&str` parameter or adding `.clone()`. (2) code trying to mutate a `Vec<String>` from inside its own loop, `for item in &vec { vec.push(...) }` — the iteration itself is an immutable borrow, so push cannot happen inside that loop; it needs to switch to pushing after the loop finishes. (3) code with a missing lifetime annotation, `fn shortest<'a>(a: &str, b: &'a str) -> &'a str` — needs distinct `'a`/`'b` lifetimes, or both parameters aligned to share one lifetime.

Try the working example

rust
// Snippet 1: fix the move error
fn print_and_return(s: String) -> String {
    println!("{s}");
    s
}

fn main() {
    let title = String::from("Ownership");
    let _ = print_and_return(title);
    // println!("{title}"); // does not compile: value moved

    // Snippet 2: fix the mutable-borrow-while-iterating error
    let mut tags = vec![String::from("rust")];
    // for t in &tags { tags.push(t.clone()); } // does not compile

    // Snippet 3: fix the missing lifetime relationship
    // fn shortest(a: &str, b: &str) -> &str { if a.len() < b.len() { a } else { b } }
}
You should see
You fix all three snippets to compile, and explain in one line each which rule was broken and how.

5-minute try-it

Fix all three snippets above so they compile — for each one, write a sentence on why you chose that particular fix (clone vs borrow vs shrinking scope).

One important caution

Trying to fix a borrow-checker error by blanket-adding `.clone()` everywhere without reading the error message — it may compile, but adds unnecessary performance cost without understanding the actual root cause.

Trying to bypass the borrow checker with an `unsafe` block instead of shrinking scope, for an error caused by mutating a collection from inside its own loop (`vec.push()`) — this breaks Rust's core safety guarantee and must be avoided.

The Rust Programming Language — References and BorrowingRust

Easy traps

  • Trying to fix a borrow-checker error by blanket-adding `.clone()` everywhere without reading the error message — it may compile, but adds unnecessary performance cost without understanding the actual root cause.
  • Trying to bypass the borrow checker with an `unsafe` block instead of shrinking scope, for an error caused by mutating a collection from inside its own loop (`vec.push()`) — this breaks Rust's core safety guarantee and must be avoided.
  • Validate sample code in a local or test environment before applying it to a production system.

Exercise

Fix all three snippets above so they compile — for each one, write a sentence on why you chose that particular fix (clone vs borrow vs shrinking scope).

You'll know it worked when: You fix all three snippets to compile, and explain in one line each which rule was broken and how.

Exercise — Fix Borrow Checker Errors | Thuta Learning