Skip to content
La3 Docs
Browse docs

Borrow Checker

Move semantics, borrow regions, aliasing rules, dangling references, drops, and MIR refinement.

Copy vs move

The checker separates trivial copyable values from values that transfer ownership.

  • Scalars, references, raw pointers, slices, ranges, and function values are copyable.
  • Strings, collections, structs, enums, and aggregates containing owned values move by default.
  • Moving a non-copy value makes the old binding unavailable.
  • Reinitializing a binding clears the moved state.

Move sites

The compiler only marks a move where the syntax and callee signature make consumption clear.

  • Binding one non-copy value to another can move it.
  • Passing a value to a by-value parameter can move it.
  • A self or mut self receiver consumes the receiver.
  • A move closure owns captured non-copy values.

Borrow regions

Current checking is sound and lexical, with more precision planned on MIR.

The frontend borrow checker treats a let-bound borrow as live until the end of its block. That can reject some safe programs, but it avoids accepting programs that could dangle or mutate through aliases.

  • &mut x forbids other access to x while live.
  • &x forbids writes to x while live.
  • Field-granular places let &user.name avoid locking user.age.
  • Returning a reference to a local is rejected.

MIR refinement

Non-lexical lifetimes and reborrows need a control-flow graph.

Some borrow facts cannot be computed soundly from the surface tree alone. Loops, back-edges, and reborrow chains need explicit control-flow and place information, which is why the more precise pass belongs on MIR.

  • NLL ends a borrow at last use instead of end of block.
  • Reborrows track that a new reference reaches through an older one.
  • The lexical pass remains the conservative guard until all relevant programs lower to MIR.