References & Borrowing
Borrowing Instead of Moving
Section titled “Borrowing Instead of Moving”Moving a value into a function is often unnecessary — you just want the function to read the value, not own it. Rust solves this with references. A reference lets you pass a value to a function without giving up ownership.
The syntax &T means “a shared reference to a value of type T”. Creating a reference is called borrowing.
fn calculate_length(s: &String) -> usize { s.len()} // s goes out of scope, but since it does not own the data, nothing is dropped
fn main() { let s = String::from("hello"); let len = calculate_length(&s); // pass a reference, not the value println!("{} has {} characters", s, len); // s is still valid}The &s syntax creates a reference to s. The function signature s: &String declares that the parameter is a reference, not an owned String.
References Do Not Own
Section titled “References Do Not Own”Because a reference does not own the value it points to, the value is not dropped when the reference goes out of scope. Ownership stays with the original variable.
fn print_greeting(name: &String) { println!("Hello, {}!", name);} // reference dropped here — name itself is unaffected
fn main() { let name = String::from("Alice"); print_greeting(&name); print_greeting(&name); // can borrow again — we still own name println!("Still have: {}", name);}Multiple Shared References
Section titled “Multiple Shared References”You can have any number of shared (&T) references to a value at the same time, as long as none of them are mutable references. Shared references are read-only.
fn main() { let s = String::from("shared"); let r1 = &s; let r2 = &s; let r3 = &s; println!("{} {} {}", r1, r2, r3); // all three are valid simultaneously}References and Function Signatures
Section titled “References and Function Signatures”When a function parameter is &String, the caller passes &my_string. When the parameter is &str (a string slice), the function is more flexible — it accepts both &String and string literals. Prefer &str over &String for function parameters as a general rule.
fn greet(name: &str) { println!("Hello, {}!", name);}
fn main() { let owned = String::from("Bob"); greet(&owned); // &String coerces to &str greet("Charlie"); // string literal is already &str}fn calculate_length(s: &String) -> usize { s.len()}
fn greet(name: &str) { println!("Hello, {}!", name);}
fn main() { let s = String::from("hello");
// Pass a reference — s is not moved let len = calculate_length(&s); println!("length of {} is {}", s, len);
// Multiple shared borrows are allowed let r1 = &s; let r2 = &s; println!("r1={} r2={}", r1, r2);
// &String coerces to &str let owned = String::from("Alice"); greet(&owned); // borrow, not move greet("Bob"); // string literal println!("owned is still: {}", owned);}Compiling…