October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run ScanOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk5 min

Reading Rust’s MIR: Following Control Flow and Values

Rust MIR turns functions into explicit control-flow graphs. Learn how to follow blocks, trace values, understand borrow-checking context, and inspect compiler output.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Rust’s Mid-level Intermediate Representation (MIR) makes a function easier to inspect as a control-flow graph: basic blocks show where execution can go, while locals, places and rvalues show where values live and how they are produced. Learning to read those pieces helps explain how rustc reasons about moves, initialization and borrows—and gives you a view between Rust source and generated code.

What MIR represents

The Rust Compiler Development Guide calls MIR “Rust’s Mid-level Intermediate Representation.” rustc constructs it from HIR, an earlier representation of the program, in a form that is simpler than Rust’s source syntax. MIR has three especially useful characteristics: it is organized around control flow, it does not use nested expressions in the same way source code does, and types are explicit.

As an Amazon Associate I earn from qualifying purchases.

That simplification is deliberate. Compiler analyses and transformations can work with explicit operations and paths through a function instead of having to reason directly about every surface feature of Rust syntax. MIR is an implementation view of rustc, not a stable language contract; its details and debugging output can change between compiler releases.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Start with the control-flow graph

Basic blocks and terminators

A MIR function is divided into basic blocks. Each block contains statements followed by a terminator. A statement performs an action and continues to the next statement in that block; the terminator ends the block and determines what happens next. A terminator may transfer control to one successor or choose among several, making branches and other control transfers explicit.

When you inspect a block, find its terminator first. It tells you which block or blocks can follow. Then read the statements above it to see what the current path changes before control moves on. This avoids treating the listing as a flat sequence when it actually describes a graph.

Locals, places and rvalues

MIR uses indexed locals as storage locations. Names such as _1 refer to locals, and _0 is used for a function’s return value. A place identifies a location that can be read or written. It can include a projection into a value, such as _1.f, which refers to a field of the local _1.

An rvalue is an expression that produces a value. In a MIR assignment, the place on the left identifies where the result goes; the rvalue on the right describes what produces it. Keeping those roles distinct is key: a place answers “where?”, while an rvalue answers “what value?” The terminology is MIR vocabulary, not a claim that the notation is ordinary Rust expression syntax.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Trace a function one block at a time

For a small MIR function, use the same questions in each block:

  1. Where can control go next? Read the block’s terminator and note its successor or possible successors.
  2. What storage location changes? Find statements that assign to a local or projected place, such as a field.
  3. What value is produced? Read the right-hand rvalue separately from the destination place.
  4. What does the next block inherit? Follow each possible edge and continue tracking the locals and places that matter to your question.

This method is useful whether you are trying to understand a branch, locate where a value is moved, or see why a borrow remains relevant along one path but not another. If a block has multiple successors, follow each path rather than assuming that every later statement executes.

Why MIR matters to borrow checking

rustc’s borrow checker operates on MIR. The compiler guide’s borrow-checking overview describes checks such as ensuring that variables are initialized before use, preventing a value from being moved twice, and rejecting a move while the value is borrowed. It also describes restrictions on accessing a place while it is mutably borrowed, other than through the reference, and on mutating a place while it is immutably borrowed.

Because MIR exposes control flow, borrow checking can reason about where a value is used or borrowed along paths through the function. The guide connects MIR-based checking to non-lexical lifetimes (NLL): lifetime regions are derived from the control-flow graph rather than being determined only by the enclosing lexical scope.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The guide’s high-level borrow-check sequence

The guide presents the implementation in broad stages. It is a useful mental model, not an exhaustive or permanent specification of rustc’s algorithm:

  1. Prepare a local copy of MIR and replace regions with inference variables.
  2. Run dataflow analyses to determine what has been moved and when.
  3. Type-check the MIR to collect region constraints.
  4. Infer region values over control-flow locations.
  5. Determine which borrows are in scope.
  6. Walk MIR again to report violations.

The important reading insight is that borrow checking combines information about values and types with information about where execution can travel. A diagnostic is not merely a textual scan of the source; the compiler’s reasoning depends on operations and paths represented in MIR.

Dataflow: tracking facts across paths

Dataflow analysis propagates facts through a control-flow graph. At a basic level, an analysis has a rule for how a block changes its incoming facts and a way to combine information arriving from different predecessors. Repeating those rules until the facts stop changing is called reaching a fixpoint. A lattice is the mathematical structure used to represent and combine such facts; a transfer function describes how an operation changes them.

The MIR dataflow chapter gives examples of rustc using dataflow to find uninitialized variables, determine which variables are live across generator yield statements, and compute which places are borrowed at a point in the control-flow graph. You do not need the formal terminology to begin reading MIR, but it helps explain how a local operation in one block can affect what the compiler knows in another.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Where MIR sits in rustc

MIR is one stage in rustc’s broader compilation process, not a single isolated conversion from source directly to machine code. The compiler overview describes parsing and successive lowering and checking stages, including lowering through THIR, followed by MIR building. MIR is then used in borrow checking and later compiler work such as optimization and code generation.

This is a useful orientation rather than a rigid one-way pipeline diagram: rustc is organized around queries and dependencies between stages. For a brief representation-level contrast, HIR is earlier and closer to source structure, MIR is simplified and explicit about control flow, and LLVM IR is a later representation involved in code generation. The exact details of each representation go beyond what that contrast establishes.

Inspect MIR with rustc debugging flags

The compiler’s MIR debugging guide documents flags for inspecting textual MIR and dataflow output. These are compiler debugging options, so check the current guide for the toolchain and channel requirements before relying on them; they are not stable interfaces guaranteed across releases.

  • -Z dump-mir writes textual MIR dumps.
  • -Z dump-mir-dataflow produces a .dot graph showing dataflow state at control-flow points.

Use a textual dump when you want to connect assignments and terminators to the function’s structure. Use the dataflow graph when your question is specifically about how an analysis’ state changes at control-flow points. In either case, begin with the basic blocks and follow successor edges before trying to interpret every detail in the output.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.