Home Explore Blog CI



rustc

1st chunk of `src/borrow_check/region_inference/lifetime_parameters.md`
9e662709037e2270e6efb379cc6c32f83108c3b320b792be000000010000095f
# Universal regions

<!-- toc -->

"Universal regions" is the name that the code uses to refer to "named
lifetimes" -- e.g., lifetime parameters and `'static`. The name
derives from the fact that such lifetimes are "universally quantified"
(i.e., we must make sure the code is true for all values of those
lifetimes). It is worth spending a bit of discussing how lifetime
parameters are handled during region inference. Consider this example:

```rust,ignore
fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> &'b u32 {
  x
}
```

This example is intended not to compile, because we are returning `x`,
which has type `&'a u32`, but our signature promises that we will
return a `&'b u32` value. But how are lifetimes like `'a` and `'b`
integrated into region inference, and how this error wind up being
detected?

## Universal regions and their relationships to one another

Early on in region inference, one of the first things we do is to
construct a [`UniversalRegions`] struct. This struct tracks the
various universal regions in scope on a particular function.  We also
create a [`UniversalRegionRelations`] struct, which tracks their
relationships to one another. So if you have e.g. `where 'a: 'b`, then
the [`UniversalRegionRelations`] struct would track that `'a: 'b` is
known to hold (which could be tested with the [`outlives`] function).


## Everything is a region variable

One important aspect of how NLL region inference works is that **all
lifetimes** are represented as numbered variables. This means that the
only variant of [`region_kind::RegionKind`] that we use is the [`ReVar`]
variant. These region variables are broken into two major categories,
based on their index:


- 0..N: universal regions -- the ones we are discussing here. In this
  case, the code must be correct with respect to any value of those
  variables that meets the declared relationships.
- N..M: existential regions -- inference variables where the region
  inferencer is tasked with finding *some* suitable value.

In fact, the universal regions can be further subdivided based on
where they were brought into scope (see the [`RegionClassification`]
type). These subdivisions are not important for the topics discussed
here, but become important when we consider [closure constraint
propagation](./closure_constraints.html), so we discuss them there.


## Universal lifetimes as the elements of a region's value

Title: Universal Regions and Lifetime Inference in Rust
Summary
This section explains how "universal regions" (named lifetimes like parameters and `'static`) are handled during region inference in Rust. All lifetimes are represented as numbered variables, divided into universal (0..N) and existential (N..M) regions. The `UniversalRegions` and `UniversalRegionRelations` structs track these regions and their relationships (e.g., `'a: 'b`). The code must be correct for all values of universal regions that satisfy the declared relationships.