# \[Pre-RFC\] Super parameter

**URL:** <https://internals.rust-lang.org/t/pre-rfc-super-parameter/22409>\
**Category:** language design\
**Created:** [February 18, 2025, 7:28pm UTC](https://internals.rust-lang.org/t/pre-rfc-super-parameter/22409 "2025-02-18T19:28:05Z")\
**Posts on this page:** 1\
**Showing post:** 5

<div class="post-metadata">

**Author:** ![steffahn](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/steffahn/32/13288_2.png) [@steffahn](https://internals.rust-lang.org/u/steffahn)\
**Post date:** [February 18, 2025, 8:41pm UTC](https://internals.rust-lang.org/t/pre-rfc-super-parameter/22409/5 "2025-02-18T20:41:28Z")

</div>

I refuse to read an AI-generated pre-RFC, even if it’s just a pre-RFC, and even though I do appreciate your transparency on the process.

Please also note that our forum TOS has a rule against machine-generated content being posted, and we tend to use that rule to say AI-generated content isn’t accepted, at least over on [users.rust-lang.org](http://users.rust-lang.org)

* * *

Like, why do I have to read this nonesense, for instance?

> [@Neutron3529](#):
>
> ## Prior Art
> 
> - **Rust RFC 2094 (Non-lexical lifetimes)**: Lays groundwork for finer-grained borrow analysis.
> - **Swift `inout` Parameters** : Modify passed values while retaining caller ownership.

The `inout` prior art of Swift is usually compared to to normal `&mut T` references in Rust, arguably. I’m not aware of any aspect of those that makes them more similar to this feature here, which seems pretty Rust specific and about lifetimes and/or about handling of truly uninitialized values during panics. Why calling out NLL should be of any relevance, isn’t clear at all.

There’s no point in reviewing a text in this form, nor calling it a Pre-RFC, if it’s still containing unreviewed AI hallucination.

* * *

This is also a **major (an obvious)** drawback: i.e. it’s re-creating a second (or maybe 3rd, if you count `fn(T) -> T`-style APIs) way of doing “inout” with vastly different syntax, comparatively little difference for many practical use-cases.

I have instead translated your prompt and will be responding mostly only to what I can read in there.

> **translated prompt (click to expand)**
>
> Please try to write an RFC in English that allows Rust to support a feature called **super parameter**.
> 
> The RFC should include the following sections: **Summary** , **Motivation** , **Guide-level explanation** , **Reference-level explanation** , **Drawbacks** , **Rationale and alternatives** , **Prior art** , and **Unresolved questions**.
> 
> The main content of the RFC can refer to [https://internals.rust-lang.org/t/discussion-super-borrow-for-super-let-placement-new-and-hybrid-borrow/22207](https://internals.rust-lang.org/t/discussion-super-borrow-for-super-let-placement-new-and-hybrid-borrow/22207). However, since the RFC is considered an evolution of that discussion, if there is any conflict between the discussion and what is stated here, please give priority to what is stated here. If there are any errors or better implementations in this RFC, please directly correct them.
> 
> * * *
> 
> This feature, called **super parameter** , will allow a Rust function that receives ownership of a variable (called a super parameter) to return the ownership of that super parameter back to the caller when the function ends. By doing so, Rust programs can create temporary borrows that can be returned to the caller.
> 
> This feature requires marking variables as super parameters, and explicitly marking the borrowing state of the super parameter when returning the variable. In the examples below, `super 'a` and `super 'a mut` are used to mark the function as having a super parameter with a certain lifetime.
> 
> ```rust
> // Here, `'a` means that when the function returns, foo is borrowed with lifetime 'a, 
> // and at most borrowed for 'a. In this syntax, the function can receive most of foo’s 
> // ownership: it can freely use &mut foo and &foo under normal borrowing rules. 
> // If and only if foo is Copy, it can use foo itself.
> 
> fn foo<'a>(super 'a foo: Foo) -> &'a Foo {
> Foo::modify(&mut foo); 
> // Since ownership of foo has been taken, we can create a mutable borrow of foo.
> 
> // drop(foo); 
> // We need to discuss whether moving this value is allowed. 
> // I tend to disallow invalidating foo because if foo is dropped here, 
> // and if the function panics before reassigning foo, then both the function 
> // and its caller would be affected. We can’t safely handle errors arising in this manner.
> 
> foo = Foo::new(); 
> // Because we have ownership, we can directly replace the original value with a new valid value.
> 
> return &foo;
> }
> 
> // Regarding the syntax sugar for self:
> impl Bar {
> pub fn sugar<'a>(super 'a self) -> &'a Self {
> todo!()
> }
> 
> // Note that super 'a self does not mean self: super 'a Self, 
> // but rather as shown below:
> pub fn full_signature<'a>(super 'a self: Self) -> &'a Self {
> todo!()
> }
> 
> // If the function does not take self, you can mark it like this:
> pub fn in_case_sugar_is_not_desired<'a>(super 'a self: Box<Self>) -> &'a Self {
> todo!()
> }
> }
> 
> ```
> 
> ### Possible use cases of this new feature include:
> 
> 1. **Allowing a mutable borrow to return an immutable borrow without affecting further immutable borrows:**  
> For example, changing `pub fn get_or_insert(&mut self, value: T) -> &T`—which affects subsequent immutable borrows of `self`—into `pub fn get_or_insert<'a>(mut 'a self, value: T) -> &'a T`, which does not interfere with continuing to borrow `self` immutably.
> 
> 2. **Enabling a more fine-grained FFI call system.**  
> For instance, when interacting with a garbage-collected language (like R), we can encapsulate all interactions with a zero-sized type (ZST). When calling R’s FFI interface to generate an unprotected value that might be garbage-collected, we mark the returned value’s lifetime as `'a`. We can still call FFI interfaces that do not trigger GC, but cannot call those that may trigger GC:
> 
> 3. **Integrating with super let, providing another way to implement super let** by possibly allowing the use of default values in the future to simulate it.
> 
> * * *
> 
> ### On whether to allow move semantics for super parameters:
> 
> Clearly, the following snippet, which may panic, cannot be safely wrapped in something like `pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R>` because, according to the function signature, `foo` must be returned valid (with at least lifetime `'a`). However, if the function panics, `foo`’s validity is not guaranteed:
> 
> ```rust
> fn foo<'a>(super 'a foo: Foo) -> &'a Foo {
> if random_bool() {
> drop(foo);
> panic!("fine.");
> } else {
> foo = Foo::new();
> }
> return &foo;
> }
> 
> ```
> 
> One idea is introducing a **panic-free** marker that guarantees the function handles all panics and returns normally, or another marker (e.g., **super fn** ) to indicate the function is not `UnwindSafe`. In that case, if the function panics, the caller cannot intercept it. However, because `pub super fn` could be confused with `pub(super) fn`, we might delay implementing move semantics for super parameters until a more appropriate marker is found.
> 
> ```rust
> // super fn means that if this function panics, the caller also panics.
> // When super fn panics, it is responsible for dropping all super parameters 
> // that haven’t been dropped yet, and ensures the caller also enters a panic state. 
> // If the caller is also a super fn, it does not drop these parameters again. 
> // By the time panic handling finishes, any super parameters that might be invalid 
> // will have been dropped exactly once, and the error handling will stop at a non-super fn. 
> // Consequently, all remaining variables are still valid.
> 
> super fn foo<'a>(super 'a foo: Foo) -> &'a Foo {
> drop(foo);
> panic!("It's fine to panic, since the caller who owns foo is panicking too.");
> }
> 
> ```
> 
> * * *
> 
> ### Super parameters and closures:
> 
> It’s uncertain whether a new trait such as `FnSuper<'a>` is needed or whether it should be combined with `FnMut`. More discussion is required on this point.
> 
> * * *
> 
> ### Some syntactic sugar:
> 
> 1. **Omitting `super`:**  
> Although under current Rust syntax omitting `super` might not cause ambiguity, it could be confusing to see `foo('a self)` vs. `foo(&'a self)`. Where there’s no confusion, we could introduce something like:
> 
> 2. **Omitting lifetimes:**  
> If there is only one super borrow and only one return lifetime, you can omit the lifetime:

From what I read in this text, this feature covers _two_ separate ideas I’ve seen discussed before:

- a way to downgrade a borrow, first mutable, then immutable for longer
  - IIRC I’ve myself come across this thought e.g. in the larger picture of [view types](https://smallcultfollowing.com/babysteps//blog/2021/11/05/view-types/), e.g. you can declare some `&{field_name} Type` reference that only partially borrows, just the field; and this could naturally extend to choosing different mutability and different lifetimes for fields, e.g. `&{'a field_1, 'b mut field_2} Type`, which is only one step removed from giving the _same_ field different lifetimes for mutable & immutable
  - on its own, such a generalization of `&mut T` references might be as simple as introducing a second lifetime, e.g. `&'a mut<'b> T` where `'a: 'b` and `'b` explains the time frame in which the reference is mutable; this could be an extension to existing `&'a mut T` by it desugaring to `&'a mut<'a> T`;
  - for more discussion, see e.g. this topic: [Pre-RFC: Downgradable mutable borrows](https://internals.rust-lang.org/t/pre-rfc-downgradable-mutable-borrows/20102)  
as well as other topics that it links to, and topics that it’s linked from

- a way (marked partially optional - i.e. your “super fn”?) to transfer/take ownership out of a reference (while avoiding the usual issues around panic safety)
  - this comes up sometimes as certain possible flavors of `&move`/`&own`-style references, e.g. here’s a thread: [Pre-RFC: Move references](https://internals.rust-lang.org/t/pre-rfc-move-references/14511)
  - this is also sometimes discussed as a possible feature of normal mutable references in a `panic=abort` configuration, e.g.: [Allow moving out of mutable references with `panic=abort`](https://internals.rust-lang.org/t/allow-moving-out-of-mutable-references-with-panic-abort/22114)
  - you superficially relate this feature to `super let`, however `super let`-like functionality, explicized into explicit parameters to determine the place the value goes would rather need some sort of `&out`-style reference; which in turn can probably already be somewhat modeled anyway, be it with APIs like the ones discussed in the `&move`-style references thread linked above, or something as simple as a `&'a mut Option<T>` which is to be called with a `None` value. And none of all of these approaches (the `&out` variable; the APIs in the other thread, or `&'mut Option<…>` or your `super 'a self`) actually prohibit  
_eventual_ direct & owned access to the value in question;  
or the choice of where else (besides a stack frame) it might live,  
so this doesn’t actually fully meet existing `fn`-level `super let` discussions which were concerned with the `pin!` macro for an example. Sound pinning can allow neither: no eventual direct owned access to the value in question, nor the choice to have it stored somewhere else than directly on a stack frame (or an `async` block’s stack, which is properly pinned with drop guarantees, too)

Other than these ideas (`&'a mut<'b> T` or `&move`-flavored reference types), your proposal doesn’t introduce any new types though. You seem to instead be proposing a new style of function parameter that can’t be expressed in types. Not a first-class citizen. Maybe there’s good reason why it’s easier this way, but it _would_ IMO feel a lot more “Rusty” to try to express such access _through a type_ instead of through new magic around functions – function coloring with `const` or `async` is already much complexity, a new parameter type also seems nicer (if possible) in this regard.

- your explanations aren’t very thorough on the limitations where and how exactly this style of function can be called, as far as I can tell. For instance, you seem to be providing not a single example where such a function is actually called. It sounds like it requires the caller to own the value… should clarify these things more: e.g. can you pass on a `super` parameter to another `super`-parameter-expecting function? etc…

- the explanation on semantics of:

- what is it about the use or manual `return` statements; is that somehow a requirement? Does the borrow always happen _in_ the `return`? Or is this just coincidence and unidiomatic style?

---

_[View the full topic](https://internals.rust-lang.org/t/pre-rfc-super-parameter/22409)._
