Exercise 4: Memory Management Debugging
Problem Statement
Debug and fix memory management issues in existing Rust code. You will:
- Fix code with ownership errors
- Resolve borrowing conflicts
- Repair code with dangling references
- Fix lifetime problems
- Optimize code to avoid unnecessary cloning
Learning Objectives
- Practice identifying common memory management errors in Rust
- Learn to read and understand Rust compiler error messages
- Apply the concepts of ownership, borrowing, and lifetimes to fix bugs
- Develop strategies for writing memory-safe code
Starter Code
// Uncomment each section one at a time and fix the issues
// Problem 1: Fix ownership errors
// fn problem1() {
// // 1.1: Fix the double-move error
// let data = vec![1, 2, 3];
// let x = data;
// let y = data; // Trying to use data after move
// println!("{:?} {:?}", x, y);
//
// // 1.2: Fix the ownership issue with the function
// let name = String::from("Rust");
// print_data(name);
// println!("My name is {}", name); // Trying to use name after move
// }
//
// fn print_data(data: String) {
// println!("Data: {}", data);
// }
// Problem 2: Fix borrowing conflicts
// fn problem2() {
// // 2.1: Fix the mutable/immutable borrow conflict
// let mut numbers = vec![1, 2, 3];
// let first = &numbers[0];
// numbers.push(4);
// println!("First element is: {}", first);
//
// // 2.2: Fix the multiple mutable borrows
// let mut data = String::from("Hello");
// let ref1 = &mut data;
// let ref2 = &mut data;
// *ref1 = String::from("Hello, ");
// *ref2 = ref2.to_string() + "Rust!";
// println!("Data: {}", data);
// }
// Problem 3: Fix dangling references
// fn problem3() {
// // 3.1: Fix the dangling reference returned by the function
// let result = get_string();
// println!("Result: {}", result);
//
// // 3.2: Fix the issue with references outliving the data
// let reference;
// {
// let data = vec![1, 2, 3];
// reference = &data;
// }
// println!("Reference: {:?}", reference);
// }
//
// fn get_string() -> &String {
// let s = String::from("I am a dangling reference");
// &s
// }
// Problem 4: Fix lifetime problems
// fn problem4() {
// // 4.1: Fix the function signature to properly handle lifetimes
// let string1 = String::from("long string is long");
// let result;
// {
// let string2 = String::from("short");
// result = longest(string1.as_str(), string2.as_str());
// }
// println!("Longest string: {}", result);
// }
//
// fn longest(x: &str, y: &str) -> &str {
// if x.len() > y.len() {
// x
// } else {
// y
// }
// }
// Problem 5: Optimize unnecessary cloning
// fn problem5() {
// // 5.1: Remove unnecessary clones while keeping the code functional
// let original = String::from("Rust Programming");
// let cloned = original.clone();
// let len = calculate_length(cloned.clone());
//
// let mut names = Vec::new();
// names.push(String::from("Alice"));
// names.push(String::from("Bob"));
//
// for name in names.clone() {
// print_string(name.clone());
// }
//
// println!("Original is still: {}", original);
// println!("Length was: {}", len);
// println!("Names: {:?}", names);
// }
//
// fn calculate_length(s: String) -> usize {
// s.len()
// }
//
// fn print_string(s: String) {
// println!("{}", s);
// }
fn main() {
println!("Uncomment and fix each problem section one by one.");
println!("Once fixed, you can run each problem function from main.");
// Uncomment these as you fix each problem:
// problem1();
// problem2();
// problem3();
// problem4();
// problem5();
}
How to Run Your Code
- First, modify the starter code in
04_memory_management_debugging_starter.rsaccording to the requirements - Run your code from the bootcamp root directory with:
cargo run --bin module2_04
Expected Output
After fixing all issues, running the code should produce output like:
Uncomment and fix each problem section one by one.
Once fixed, you can run each problem function from main.
# Problem 1 output:
[1, 2, 3] [1, 2, 3]
Data: Rust
My name is Rust
# Problem 2 output:
First element is: 1
Data: Hello, Rust!
# Problem 3 output:
Result: I am a dangling reference
Reference: [1, 2, 3]
# Problem 4 output:
Longest string: long string is long
# Problem 5 output:
Alice
Bob
Original is still: Rust Programming
Length was: 16
Names: ["Alice", "Bob"]
Tips
- Read the Rust compiler errors carefully - they often tell you exactly what's wrong
- Consider using
clone()when you need to duplicate data, but remove clones when unnecessary - Remember to use references (
&and&mut) to avoid transferring ownership - Use lifetimes when functions need to return references
- Think about the scope of variables and when they are dropped
- For problems with dangling references, consider returning owned data or extending lifetimes