Mandatory inlined functions for unsized types and 'super lifetime?

The main thing needed to do that, and imho, it wouldn't really need that much sugar, would be existential types in argument position.

We could achieve that with type slot_a = impl Sized; and then taking an &'a mut Option<slot_a> that we .get_or_insert()-initialize in the callee's body, but I have tested that and currently, a get_or_insert() does not result in a defining usage of the existential type, it results, instead in a cycle.


The issue I see with this sugar, is the lack of control over when exactly the caller-allocated memory is allocated. For instance, how would that work with:

let mut v = vec![];
for _ in 0 .. n {
    v.push(f2());
}

We agree that that should not compile, right? (None slot reused).

And yet an if cond() { v.push(f2()); } ideally would compile (None slot could be put outside the if).

  • this would, however, result in that much stack usage even when cond() == false.

So the sugar should, at least, offer some way to manually provide the slot, so as to control its lifetime.

And I feel that we don't gain that much compared to manually writing the &mut Option slots; the way to gain some sugar could come from a proc-macro at that point:

#[with_slots(a: 'a)] // fn f2<'a> (a: &'a mut Option<_>, …) -> &'a [u8]
fn f2 (…) -> &'a [u8]
{
    let (a, b) = #[with_slots(a, _)] f3(…); // f3((a, &mut None), …);
    …
}

#[with_slots(a: 'a, b: 'b)] // f3<'a, 'b> ((a: …, b: …), …)
fn f3 (…) -> (&'a [u8], &'b [u8])
{
    set!(a = …); // let a = a.get_or_insert(…);
    set!(b = …);
    // …
    (a, b)
}

fn advanced (v: &mut Vec<_>)
{
    slot!(a); // let ref mut a = None;
    if cond() {
        v.push(#[with_slots(a)] f2(…));
        // or:
        v.push(f2(a, …));
    }
}