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, …));
}
}