In this lesson, you will learn how to improve your program’s performance by passing references instead of triggering expensive data copies.
In Rust, the Clone trait allows you to create a duplicate of an object. While this is sometimes necessary, calling .clone() tells the computer to allocate new memory and copy every bit of data from your original object into a new location. If your object contains a large String or a Vec with thousands of items, this can significantly slow down your application.
The "Borrowing" Alternative
Instead of duplicating the data, you can simply "borrow" it. By passing a reference (&T) to a function, you allow the function to look at or use the data without actually taking ownership or creating a duplicate.
Consider this inefficient pattern:
fn print_message(msg: String) {
println!("{}", msg);
}
fn main() {
let my_data = String::from("Heavy data load...");
// Inefficient: Cloning the heap data just to print it
print_message(my_data.clone());
// my_data is still here, but we wasted memory/time
println!("{}", my_data);
}
In the example above, my_data.clone() creates a complete duplicate of the string on the heap. If you call this in a loop, your program will spend more time copying bytes than doing actual work.
Optimizing with References
By changing the function signature to accept a reference, you eliminate the need for clone() entirely:
fn print_message(msg: &String) {
println!("{}", msg);
}
fn main() {
let my_data = String::from("Heavy data load...");
// Efficient: Passing a reference (pointer) instead of copying data
print_message(&my_data);
println!("{}", my_data);
}
When you pass &my_data, you are only passing a tiny, fixed-size reference. The heavy heap memory stays exactly where it is. This is not only faster, but it also consumes significantly less memory. Whenever you see .clone() in your code, ask yourself: "Do I actually need a new, independent copy, or do I just need to read the data?" If it’s the latter, reach for a reference instead.
Key Takeaway: Prefer passing references to functions when you only need to read data, as this avoids the memory and CPU costs of duplicating heap-allocated structures.