{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreid43kgvaoqxcnagx5ydkdgn4hc7bid2ctl4q6rli7cvsg5ked7jxy",
"uri": "at://did:plc:ivbknywyskln22er3nkssdhl/app.bsky.feed.post/3mh7rqdf6kcx2"
},
"path": "/t/private-lifetime-inference/24088#post_1",
"publishedAt": "2026-03-16T20:45:46.000Z",
"site": "https://internals.rust-lang.org",
"textContent": "# [Pre-RFC] Lifetime inference for non-public items\n\n## Summary\n\nAllow 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.\n\n## The problem\n\nPrivate code pays the same lifetime annotation tax as public API, for zero benefit to crate consumers.\n\n**Functions** — the classic case where elision is insufficient:\n\n\n // Today: must annotate manually, even though it's private\n fn find_best<'a>(primary: &'a Data, fallback: &'a Data) -> &'a Summary {\n if primary.is_valid() { &primary.summary } else { &fallback.summary }\n }\n\n\n**Structs** — this is where the pain really compounds:\n\n\n // Private struct — 'a is obvious from the fields\n struct ParseState<'a> {\n input: &'a str,\n tokens: &'a [Token],\n current: &'a Token,\n }\n\n // Now EVERY function must propagate 'a:\n fn parse_expr<'a>(state: &mut ParseState<'a>) -> Expr<'a> { ... }\n fn parse_binary<'a>(state: &mut ParseState<'a>) -> Expr<'a> { ... }\n fn parse_atom<'a>(state: &mut ParseState<'a>) -> Expr<'a> { ... }\n // ... repeat 20 more times\n\n\nChange one lifetime in `ParseState` → update 20+ function signatures. The compiler knows the right answer. Why am I doing this by hand?\n\n**Enums, type aliases, impl blocks** — same story:\n\n\n enum CacheEntry<'a> { Hit(&'a Response), Miss }\n type Pair<'a> = (&'a str, &'a str);\n impl<'a> ParseState<'a> { fn advance(&mut self) -> &'a Token { ... } }\n\n\n## The proposal\n\nFor items **not reachable from outside the crate** , the compiler infers lifetimes:\n\n\n // All private — no lifetime annotations needed:\n\n struct ParseState {\n input: &str,\n tokens: &[Token],\n current: &Token,\n }\n\n impl ParseState {\n fn advance(&mut self) -> &Token { ... }\n }\n\n fn parse_expr(state: &mut ParseState) -> Expr { ... }\n\n enum CacheEntry { Hit(&Response), Miss }\n\n\nFor `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.\n\n## Why this makes sense\n\n 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.\n\n 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.\n\n 3. **Precedent in other languages.** Kotlin and Swift require explicit types on public API but infer for private code. The visibility-based boundary works.\n\n 4. **The crate boundary is already special.** Type privacy (RFC 2145), `impl Trait` opacity, auto-trait leakage, orphan rules — all use the same boundary.\n\n\n\n\n## Visibility rule\n\nVisibility | Inference?\n---|---\nprivate, `pub(self)`, `pub(super)`, `pub(crate)` | Yes\n`pub` at crate boundary | No\n`pub` trait method declarations | No\n\n## Open questions\n\n * **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.\n * **Should`pub(crate)` be included?** I think yes — it's still internal to the crate. But open to discussion.\n * **Private traits** : inferring method lifetimes from implementations is the most complex part. Worth deferring to a follow-up?\n * **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.\n\n\n\n## What I'm looking for\n\nFeedback on:\n\n * Is this worth pursuing as an RFC?\n * What scope makes sense for a first proposal (functions only vs. all items)?\n * Any technical blockers I'm missing?\n * Pointers to prior discussions on this topic\n\n\n\nI have a full RFC draft ready if there's interest.",
"title": "Private lifetime inference"
}