Activity 16 - Borrowing and References Debugging

Fix the borrowing bugs in each of the following code snippets. Try pasting them in the Rust playground and work on them until they compile and run.

When you have working code, paste the corrected code, with a comment on the line you fixed saying why your fix works.

Warm-up Problems

Problem 1: Use After Move

fn main() {
    let data = vec![1, 2, 3];
    print_data(data);
    println!("{:?}", data); // Fix this!
}

fn print_data(v: Vec<i32>) {
    println!("{:?}", v);
}

Hint: The function takes ownership. How can you let it borrow instead?

Problem 2: Reference Confusion

fn main() {
    let scores = vec![85, 92, 78];
    let first = scores[0];  // This works, but...

    let names = vec![String::from("Alice")];
    let first_name = names[0];  // This doesn't! Fix it

    println!("First score: {}", first);
    println!("First name: {}", first_name);
}

Hint: What's different about i32 vs String? How can you access the String without moving it?

Problem 3: Iterator Ownership

fn main() {
    let nums = vec![1, 2, 3];
    for n in nums {
        println!("{}", n * 2);
    }
    println!("{:?}", nums); // Oops! Fix the loop
}

Hint: How can you iterate without consuming the vector?

Problem 4: Multiple Functions Need the Same Data

fn main() {
    let message = String::from("Hello, Rust!");

    let len = get_length(message);
    let upper = to_uppercase(message);

    println!("Length: {}, Uppercase: {}", len, upper);
}

fn get_length(s: String) -> usize {
    s.len()
}

fn to_uppercase(s: String) -> String {
    s.to_uppercase()
}

Hint: Both functions try to take ownership. What if they borrowed instead?

Problem 5: Iterator Pattern Matching

fn main() {
    let pairs = vec![(1, 2), (3, 4), (5, 6)];

    for (a, b) in pairs.iter() {
        let sum = a + b;  // Error! Can't add references
        println!("{} + {} = {}", a, b, sum);
    }

    println!("Pairs still available: {:?}", pairs);
}

Hint: What type does .iter() give you? The tuple pattern (a, b) doesn't automatically dereference. How can you extract the values from the references?

Challenge Problems

Problem 6: Complex Ownership Chain

fn main() {
    let data = vec![10, 20, 30, 40, 50];
    let result = process(data);

    println!("Original: {:?}", data);  // Want to keep using data!
    println!("Result: {:?}", result);
}

fn process(nums: Vec<i32>) -> Vec<i32> {
    let popped = pop_last(nums);
    push_7(popped)
}

fn pop_last(nums: Vec<i32>) -> Vec<i32> {
    nums.pop();
    nums
}

fn push_7(mut nums: Vec<i32>) -> Vec<i32> {
    nums.push(7);
    nums
}

Hint: Where can you borrow or clone instead of passing ownership?

Problem 7: Function Returns and Borrowing

fn main() {
    let data = vec![5, 10, 15, 20];
    let largest = find_largest(&data);

    data.push(25);  // Error! Fix this

    println!("Largest was: {}", largest);
    println!("Updated data: {:?}", data);
}

fn find_largest(numbers: &Vec<i32>) -> &i32 {
    let mut largest = &numbers[0];
    for num in numbers.iter() {
        if num > largest {
            largest = num;
        }
    }
    largest
}

Hint: The function returns a reference into the vector. How long does that borrow last? Would it be safe to modify the vector while that reference exists? (We'll learn the precise rules for this next time.)