{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreifhc5wp2pzq4sxrh3vybbnntr7nkpqswdnacljgyrhu7lx3uytlvu",
"uri": "at://did:plc:ivbknywyskln22er3nkssdhl/app.bsky.feed.post/3mhfhaa5tej32"
},
"path": "/t/idea-borrowck-transparent-function-calls/24103#post_1",
"publishedAt": "2026-03-19T06:33:53.000Z",
"site": "https://internals.rust-lang.org",
"tags": [
"View Types",
"Macro Functions",
"Lifetime inference for non-public items"
],
"textContent": "This is mostly a vibe-check on the idea.\n\n## Motivation\n\nBasically, this idea is meant to allow following usage\n\n\n impl Foo {\n pub fn wham(&mut self) {\n for baz in &mut self.baz {\n if self.has_bar() {\n // ^^^^ error with current rust\n *baz += \"1\";\n }\n\n if let Some(bar) = self.first_bar_mut() {\n // ^^^^ error with current rust\n *bar += baz;\n }\n }\n }\n\n fn has_bar(&self) -> bool {\n !self.bar.is_empty()\n }\n\n fn first_bar_mut(&mut self) -> Option<&mut String> {\n self.bar.first_mut()\n }\n }\n\n\nThis comes up a lot when writing helper functions. And currently you need to either:\n\n * Somehow split you struct into smaller structs. But\n 1. This is tedious since it will impact basically all your code.\n 2. It's not always possible to do it (reasonably).\n * Just inline the function manually.\n 1. This can get quite verbose and unmaintable.\n 2. As soon as helper function body is longer than 5 tokens, the readablity suffers.\n\n\n\n## Idea\n\nTransparent function calls (TFC) let borrow-checker see through private function calls, using it's body to borrow check instead of it's signature.\n\n..and private function calls could means one of below:\n\n * All calls to all private functions.\n * All calls to private functions with a certain attribute (`#[bikesheld_transparent]`) annotated. (This is my perference.)\n * All calls to any self crate defined functions with a certain attribute annotated.\n * Add a new function calls syntax to private functions.\n\n\n\nMaybe there's some more variants, but the core idea is downstream crate can't see through, so there's no accidental public api breakage.\n\nThe called function itself is still a regular function.\n\n## Difference to Previous Similiar Ideas\n\nFollowing ideas also solves the problem showed in the motivation part. The main difference is that, transparent function calls are much more limited, and introduce minimal new syntax.\n\n### View Types\n\nView types is a better solution for public facing apis or traits, but it introduce much more complexity to the type system, so it's inheritly harder to move forward. And we really don't want to annotate view type for helper functions, for obvious reasons (versatility, verbosity, etc.).\n\n### Macro Functions\n\nMacro functions's scope is much larger, and is somewhat controversial due to it's template-esque nature. TFCs are _not_ intended to support any duck typing use cases, or any type inference.\n\n### Lifetime inference for non-public items\n\nActually the motivation of this transparent function calls post. And the most similiar one. Core difference is tfc doesn't try to infer anything, and only affect function items.\n\n## Misc details\n\n * To handle recursive TFC, I imagine some kind of inlining happening at MIR level. Haven't think through all these but I think it's doable.\n * Whatever the restriction is, TFC cannot happen across crate.\n * Whether trait methods of local type could be called transparently is undecided yet. I lean on yes if the implementation is local as well, but it's complicated.\n\n\n\n## Further Extensions\n\nThese extensions are what I think is reasonable to add as well. But the TFC can live without them. So I did include them in the main idea.\n\n### `#[bikesheld_force_transparent]`\n\nYou can annotate a private (or non-pub?) function with `#[bikesheld_force_transparent]`. This makes the function not implementing `Fn` family traits, and is always called transparently. Example:\n\n\n #[bikesheld_force_transparent]\n fn longest(left: &str, right: &str) -> &str {\n if left.len() > right.len() {\n left\n } else {\n right\n }\n }\n\n\nNote that since it's always called transparently, you could omit lifetime constraints on signature.\n\n### Variadic Lifetime Parameter Elision Syntax\n\nIntroduce `'..` (bikesheld syntax) as a shorthand for any replication of `'_` parameter in function signature. Examples:\n\n\n fn foo(r: Ref<'.., T>) {} // Ref<'a, T>\n fn bar(c: Ctx<'..>) {} // Ctx<'env, 'scope>\n fn bad1(a: Ctx<'env, '..>) // Error: `'..` cannot be used along side other litime\n fn bad2(a: Ctx<'.., 'scope>) // Error: `'..` cannot be used along side other litime\n fn degenerate(s: String<'..>, v: Vec<'.., i32>) // Ok. There's zero `'_`.\n\n\nThis is meant to solve \"add a reference field in one struct, every function got infested\" problem. Maybe it deserve a seperate RFC.\n\n## End\n\nThis is a rough idea that I've had for quite a bit of time. Is this a total no-go? Or it's a too limited to be useful? Or it should add even more limitations? I'd love to hear some feedback!",
"title": "Idea: Borrowck Transparent Function Calls"
}