In this lesson, you will learn how the Rust compiler evolved to become smarter about when a reference’s "lifetime" actually ends.
The Problem: Lexical Scopes
In the early days of Rust, the borrow checker was strictly "lexical." This meant that if you created a reference, the compiler considered it "in use" until the end of the scope (the closing curly brace }).
This was often too restrictive. Imagine you have a mutable reference that you only use for one line, but you have a print statement at the very end of the function. Under old rules, the compiler would prevent you from creating a second, immutable reference anywhere in between, even if the mutable reference was never used again. It acted as if the "borrow" lasted until the function exited.
The Solution: Non-Lexical Lifetimes (NLL)
To fix this, the Rust team introduced Non-Lexical Lifetimes (NLL). Instead of looking at scope blocks, the compiler now performs "control-flow analysis." It tracks the actual last time a reference is used. Once the last usage of a reference passes, the borrow checker considers that reference "dead," even if the variable hasn't gone out of scope yet.
Consider this example:
let mut data = vec![1, 2, 3];
let m = &mut data; // Mutable borrow starts
m.push(4);
// NLL realizes 'm' is never used again here!
let r = &data; // This is now safe!
println!("{:?}", r);
Before NLL, the code above would fail to compile because m would be considered "alive" until the end of the block, conflicting with the shared reference r. With NLL, the compiler understands that m effectively ended after m.push(4), allowing r to be created safely.
This evolution didn't change the core safety rules—you still cannot have mutable and immutable references to the same data at the same time—but it made the rules feel much more natural and less frustrating for everyday coding. You no longer have to manually wrap variables in inner scopes {} just to satisfy the borrow checker's outdated requirements.
Key Takeaway: Non-Lexical Lifetimes allow the compiler to end a borrow as soon as the reference is last used, rather than forcing it to persist until the end of the current scope.