{
  "$type": "site.standard.document",
  "bskyPostRef": {
    "cid": "bafyreid43kgvaoqxcnagx5ydkdgn4hc7bid2ctl4q6rli7cvsg5ked7jxy",
    "uri": "at://did:plc:ivbknywyskln22er3nkssdhl/app.bsky.feed.post/3mha767zyylh2"
  },
  "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"
}