Pimp My IDE / garage dispatchBack to the garage
October 13, 2026 / compiler contracts / Rust

A green build can still owe you memory safety.

A new Rust issue shows safe-looking field assignment crossing a lifetime bound that a destructor later trusts.

Do not turn one compiler report into a panic about every Rust program. Preserve the reproducer, record the compiler version, and separate acceptance from runtime evidence.

The report is narrow and serious.

Rust issue 164250 reports that a generic struct declared with a 'static bound can accept a reference to a local value through field assignment. The attached program compiles, then keeps that reference during Drop. Its ordinary run prints data from reused stack storage. The AddressSanitizer run reports a stack use after scope.[1]

The issue was open when this dispatch was written. The Rust project labels it as a soundness bug in the borrow checker, lifetime, variance, destructor, and types areas. Its current priority label is high. Comments trace variants back to old compiler releases, but the issue does not yet provide a released fix or a complete impact census.[1]

The compiler accepted the program. The runtime witness showed why acceptance was wrong.

Variance is the lane change.

The Rust Reference says lifetime subtyping can occur during type checking. A reference with a longer lifetime can be used where a shorter lifetime is expected. Variance controls whether that relation passes through a generic type.[2]

That machinery is useful. It lets references fit the lifetime a caller needs. It also means a field update cannot be checked as a local syntax event. The compiler must preserve every bound that the enclosing type and its destructor will rely on later.

struct Q<T: 'static>(Option<T>);

let local = String::from("short life");
let mut q = Q(None);
q.0 = Some(&local); // reported as accepted
// q is dropped after local leaves its valid scope

Drop turns the bound into behavior.

The Rustonomicon explains why generic destructors receive stricter lifetime checks. A Drop implementation can read borrowed data during destruction. Its generic arguments must outlive the value when the destructor can observe them.[3]

The reported example makes that ability concrete. The type declaration says T: 'static. The field assignment installs a shorter reference. The destructor moves the value into longer-lived storage. The stale reference is then read after its source scope ended.[1]

Do not reduce this to "Rust is unsafe" or "the borrow checker is broken." The useful engineering question is whether your compiler accepted this shape, which versions did so, and what changed after a fix lands.

Build the regression record before the fix arrives.

Save the exact source, compiler identity, command, acceptance result, and runtime witness. Run the same fixture against the version you ship and the candidate fixed version. Keep the compile result separate from the sanitizer result. A new rejection proves the compiler closed this reproducer. It does not prove that every related path is closed.

The Rust compiler guide describes variance as a query that computes how generic parameters may vary. That is the implementation layer maintainers will inspect. Application teams do not need to guess the patch. They need a small fixture and a versioned before-and-after record.[4]

If you maintain generic types with custom destructors, add nearby tests for constructor use, field assignment, mutation through references, and drop order. Keep each test small enough that a changed compiler verdict has one readable cause.

Interactive makeover / checklist plus causal route plus receipt

Load the recall rack.

A plain checklist hides test order. This rack connects source custody, compiler identity, compile verdict, and runtime witness. Select the sections your regression packet will require. The rack drafts a plan. It does not run Rust or verify a fix.

1Source
2Compiler
3Compile
4Runtime

Select packet sections

Independent square switches choose which evidence fields the packet will request.

Inspect the route

No sections selected. The packet has no custody or result fields yet.

EVIDENCE BUS / CONTIGUOUS ROUTE REQUIRED
0 SECTIONS SELECTEDSelect source custody first. Later sections without earlier evidence leave a gap.

A complete route means the template asks for all four records. It does not mean the compiler was tested or the issue was fixed.

Sources read / October 13, 2026

Source log

Open the issue, language contract, compiler guide, and discussion record
  1. rust-lang/rust issue 164250, "struct's lifetime requirements can be bypassed via covariant field assignment", opened and read October 13, 2026. This is the primary report for the reproducer, observed output, AddressSanitizer result, issue state, and project labels. The issue was open with a high-priority label when read.
  2. The Rust Reference, "Subtyping and variance", read October 13, 2026. This supplies the language-level description of lifetime subtyping and variance.
  3. The Rustonomicon, "Drop Check", read October 13, 2026. This supports the explanation of why generic destructors must preserve lifetime relationships for data they may observe.
  4. Rust Compiler Development Guide, "Variance", read October 13, 2026. This supplies the compiler implementation context for variance inference. It does not document the fix for issue 164250.
  5. Hacker News discussion, item 50069499, verified through the Hacker News API and read October 13, 2026. This was the discovery route. It is not used as language or compiler evidence.

Evidence boundary. This dispatch reports the linked issue and the contracts around it. It did not execute the reproducer, inspect a compiler patch, or establish which production programs are affected. The interactive rack is a browser-local planning tool.