Preventing Dangling References
In this page:
fn name() -> Type {
let value = data;
value // return the owned value, not &value
}
What a Dangling Reference Would Be
A dangling reference points to memory that has already been freed. In languages like C, this can silently cause undefined behavior; Rust's compiler catches and rejects such code before it ever runs.
Example: What a Dangling Reference Would Be
// Define the function `valid_reference` with no parameters and returning a String
fn valid_reference() -> String {
// Declare an immutable variable `s`, set to `String::from("owned, not dangling")`
let s = String::from("owned, not dangling");
s
}
// Entry point: execution of the program starts here
fn main() {
// Declare an immutable variable `result`, set to `valid_reference()`
let result = valid_reference();
// Print "{}" to the console, substituting in `result` (with a trailing newline)
println!("{}", result);
}
Login to try C/C++/Java/PHP code in the editor
Why Returning a Reference to a Local Fails
If a function tried to return a reference to a variable it created locally, that variable would be dropped when the function ends, leaving the reference pointing at freed memory -- so Rust's compiler refuses to compile it.
Example: Why Returning a Reference to a Local Fails
fn safe_owned_return() -> String {
let local = String::from("built locally");
local // returning ownership, not a reference, is safe
}
fn main() {
let value = safe_owned_return();
println!("{}", value);
}
Login to try C/C++/Java/PHP code in the editor
The Fix: Return Owned Data
The standard solution when you would otherwise create a dangling reference is to return an owned value instead, transferring ownership out of the function so the data survives past the function call.
Example: The Fix: Return Owned Data
// Define the function `build_message` taking `name` and returning a String
fn build_message(name: &str) -> String {
format!("Hello, {}!", name)
}
// Entry point: execution of the program starts here
fn main() {
// Declare an immutable variable `message`, set to `build_message("Rust")`
let message = build_message("Rust");
// Print "{}" to the console, substituting in `message` (with a trailing newline)
println!("{}", message);
}
Login to try C/C++/Java/PHP code in the editor
References Tied to Their Data's Lifetime
As long as a reference is used only while its underlying data is still alive (still owned by something in scope), it is completely safe, and this is exactly what the compiler checks for every reference in your program.
Example: References Tied to Their Data's Lifetime
// Entry point: execution of the program starts here
fn main() {
// Declare an immutable variable `text`, set to `String::from("alive for the whole scope")`
let text = String::from("alive for the whole scope");
// Declare an immutable variable `reference`, set to `&text`
let reference = &text;
// Print "{}" to the console, substituting in `reference` (with a trailing newline)
println!("{}", reference);
}
Login to try C/C++/Java/PHP code in the editor
- Trying to return a reference to a local variable created inside the function, which would go out of scope on return.
- Assuming Rust allows dangling pointers like C does, just with warnings -- Rust refuses to compile such code at all.
- Fixing a dangling reference error by returning an owned value instead, but forgetting to update the function's return type.
- A dangling reference points to memory that has already been freed; Rust's compiler rejects such code entirely.
- The borrow checker tracks how long data lives (its lifetime) and ensures references never outlive their data.
- The typical fix for a would-be dangling reference is to return an owned value instead of a reference.
- This guarantee is enforced entirely at compile time, with no runtime checks needed.
Chapter Quiz — Complete all 6 topics to unlock
0/6 topics done
Complete these topics first: