External Publication
Visit Post

Private lifetime inference

Rust Internals [Unofficial] March 16, 2026
Source

[Pre-RFC] Lifetime inference for non-public items

Summary

Allow the compiler to infer lifetime parameters for all items (functions, structs, enums, type aliases, impl blocks) that are not pub at the crate boundary. Public API continues to require explicit annotations as today.

The problem

Private code pays the same lifetime annotation tax as public API, for zero benefit to crate consumers.

Functions — the classic case where elision is insufficient:

// Today: must annotate manually, even though it's private
fn find_best<'a>(primary: &'a Data, fallback: &'a Data) -> &'a Summary {
    if primary.is_valid() { &primary.summary } else { &fallback.summary }
}

Structs — this is where the pain really compounds:

// Private struct — 'a is obvious from the fields
struct ParseState<'a> {
    input: &'a str,
    tokens: &'a [Token],
    current: &'a Token,
}

// Now EVERY function must propagate 'a:
fn parse_expr<'a>(state: &mut ParseState<'a>) -> Expr<'a> { ... }
fn parse_binary<'a>(state: &mut ParseState<'a>) -> Expr<'a> { ... }
fn parse_atom<'a>(state: &mut ParseState<'a>) -> Expr<'a> { ... }
// ... repeat 20 more times

Change one lifetime in ParseState → update 20+ function signatures. The compiler knows the right answer. Why am I doing this by hand?

Enums, type aliases, impl blocks — same story:

enum CacheEntry<'a> { Hit(&'a Response), Miss }
type Pair<'a> = (&'a str, &'a str);
impl<'a> ParseState<'a> { fn advance(&mut self) -> &'a Token { ... } }

The proposal

For items not reachable from outside the crate , the compiler infers lifetimes:

// All private — no lifetime annotations needed:

struct ParseState {
    input: &str,
    tokens: &[Token],
    current: &Token,
}

impl ParseState {
    fn advance(&mut self) -> &Token { ... }
}

fn parse_expr(state: &mut ParseState) -> Expr { ... }

enum CacheEntry { Hit(&Response), Miss }

For pub items at crate boundary — everything stays as today. When you change a private item to pub, the compiler tells you to add annotations and suggests the correct ones.

Why this makes sense

  1. Closures already do this. |x: &str, y: &str| -> &str { x } infers lifetimes from the body. Private named items are equally invisible to crate consumers.

  2. Precedent in Rust. RFC 2093 added inference of T: 'a bounds on structs. Variance inference is a whole-crate fixed-point analysis that works silently. This is the same philosophy.

  3. Precedent in other languages. Kotlin and Swift require explicit types on public API but infer for private code. The visibility-based boundary works.

  4. The crate boundary is already special. Type privacy (RFC 2145), impl Trait opacity, auto-trait leakage, orphan rules — all use the same boundary.

Visibility rule

Visibility Inference?
private, pub(self), pub(super), pub(crate) Yes
pub at crate boundary No
pub trait method declarations No

Open questions

  • Struct inference strategy : infer lifetimes purely from field types (simple, local) or also from usage sites (precise, whole-crate)? Variance inference already does whole-crate analysis, so there's precedent.
  • Shouldpub(crate) be included? I think yes — it's still internal to the crate. But open to discussion.
  • Private traits : inferring method lifetimes from implementations is the most complex part. Worth deferring to a follow-up?
  • Phased approach : start with functions only? Or include structs/enums from the start? The struct case is where the biggest ergonomic win is, so I'd argue for including it.

What I'm looking for

Feedback on:

  • Is this worth pursuing as an RFC?
  • What scope makes sense for a first proposal (functions only vs. all items)?
  • Any technical blockers I'm missing?
  • Pointers to prior discussions on this topic

I have a full RFC draft ready if there's interest.

Discussion in the ATmosphere

Loading comments...