Region Inference
Tutorial Video
Text description (accessibility)
This video demonstrates the "Region Inference" functional Rust example. Difficulty level: Intermediate. Key concepts covered: Functional Programming. Rust's borrow checker models each reference as having a "region" — a set of program points at which the reference is valid. Key difference from OCaml: 1. **Tracking granularity**: Rust's region inference is per
Tutorial
The Problem
Rust's borrow checker models each reference as having a "region" — a set of program points at which the reference is valid. The compiler infers the minimal region sufficient to cover all uses of the reference, rather than using a static scope. Region inference is the theoretical foundation that makes both lexical lifetimes and NLL work. Understanding how the compiler infers regions helps predict what the borrow checker will accept, explains "does not live long enough" errors, and motivates the Polonius project which extends region inference to handle more cases soundly.
🎯 Learning Outcomes
Code Example
#![allow(clippy::all)]
//! Region Inference
//!
//! How the compiler infers lifetime regions.
/// Compiler infers minimal region for borrow.
pub fn inferred_region() {
let mut x = 5;
let r = &x; // region starts
let _ = *r; // region ends (last use)
x = 10; // OK: region ended
assert_eq!(x, 10);
}
/// Region spans usage.
pub fn region_span(data: &[i32]) -> i32 {
// 'a region covers entire function
data.iter().sum()
}
/// Nested regions.
pub fn nested_regions(v: &mut Vec<i32>) {
{
let r = &v[0..2]; // inner region
let _ = r.len();
}
v.push(10); // outer region continues
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_inferred() {
inferred_region();
}
#[test]
fn test_span() {
let data = [1, 2, 3];
assert_eq!(region_span(&data), 6);
}
#[test]
fn test_nested() {
let mut v = vec![1, 2, 3];
nested_regions(&mut v);
assert_eq!(v.len(), 4);
}
}Key Differences
OCaml Approach
OCaml has no region inference. The GC uses a reachability-based model: a value is alive as long as any path from a root (stack variable, global) can reach it. This is simpler than region inference — no program-point-level tracking is needed.
let region_demo () =
let v = [| 1; 2; 3; 4; 5 |] in
let r = v.(0) in (* no "region" — just a copy *)
(* can modify v freely — r is just an int *)
v.(0) <- 10;
r (* returns original value *)
Full Source
#![allow(clippy::all)]
//! Region Inference
//!
//! How the compiler infers lifetime regions.
/// Compiler infers minimal region for borrow.
pub fn inferred_region() {
let mut x = 5;
let r = &x; // region starts
let _ = *r; // region ends (last use)
x = 10; // OK: region ended
assert_eq!(x, 10);
}
/// Region spans usage.
pub fn region_span(data: &[i32]) -> i32 {
// 'a region covers entire function
data.iter().sum()
}
/// Nested regions.
pub fn nested_regions(v: &mut Vec<i32>) {
{
let r = &v[0..2]; // inner region
let _ = r.len();
}
v.push(10); // outer region continues
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_inferred() {
inferred_region();
}
#[test]
fn test_span() {
let data = [1, 2, 3];
assert_eq!(region_span(&data), 6);
}
#[test]
fn test_nested() {
let mut v = vec![1, 2, 3];
nested_regions(&mut v);
assert_eq!(v.len(), 4);
}
}#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_inferred() {
inferred_region();
}
#[test]
fn test_span() {
let data = [1, 2, 3];
assert_eq!(region_span(&data), 6);
}
#[test]
fn test_nested() {
let mut v = vec![1, 2, 3];
nested_regions(&mut v);
assert_eq!(v.len(), 4);
}
}
Deep Comparison
OCaml vs Rust: lifetime region inference
See example.rs and example.ml for implementations.
Exercises
inferred_region and add comments to every line marking which references are in scope at that point — identify where each region starts and ends.v[i], uses it, then modifies v[i+1] — identify whether the borrow region covers the whole loop iteration or ends at the borrow's last use.