Say you have a handful of coroutines and they all want exclusive access to some state during their interleaved executions. Passing &mut state through the coroutine argument provides an elegant way to solve this problem without the need for RefCells.
We can actually use the semantics of the implicit argument (excluding the implicitness) and apply them to all arguments to generators. So modifying vadimcnâs proposal in this way would mean that yield returns (), but arguments refer to value given by the latest resumption. Effectively this makes yield assign to all argument bindings (although that isnât what happens at runtime). Borrows of arguments must not cross suspend points, which is only a restriction in immovable generators. This allows arguments to contain types which cannot cross suspend points (like for<'a> &'a T) and also wouldnât consider arguments part of the generator frame (which is used for OIBIT trait selection). It also avoids the asymmetric |mut arg| { (arg,) = yield; } or the very noisy (a1, a2, a3, ...) = yield.
I think I had something like this in my RFC under Implicitly binding coroutine arguments. One relatively small drawback is that itâd be harder to write a macro that wraps yield and needs to use the passed-in value(s).
BTW, here are some other ideas Iâve had for dealing with let (arg,) = yield:
- Implement Deref/DerefMut on 1-tuples. Youâd then write
let arg = yield, and theargwould be a tuple, but auto-deref would allow you to use it in place of the inner value like 90% of the time. The rest of the time, youâd have to usearg.0. - Alter type inference such that 1-tuple containing type T is allowed to unify with T and insert automatic tupling/un-tupling if that happens (a more transparent version of #1, basically). I am not sure about the overall impact of this on type inference, however.
let (arg1, arg2, arg3) = yield doesnât bother me nearly as much as the single-argument case.
We know the "arity" of a coroutine before we type-check its body. It seems to me that we can just special-case the single argument case, as well. I could live with that myself.
The only problem I could imagine is that "code generation utilities" would also have to cope with this irregularity. It's unclear to me how big of a problem this would be.
Iâve opened an experimental RFC for stackless coroutines as a result of the discussion in todayâs compiler team meeting. The intention here is that this is explicitly experimental and pretty hand-wavy on the details. A future RFC will be required for stabilization but hopefully this RFC can go through the process much more quickly.
Wouldnât
let x = yield.0;
be quivalent to
let (x,) = yield;
?
What if we want to eventually have other types than tuples as arguments, so we can e.g. emulate named arguments with anonymous structs? (if not then special casing single arg tuple is ok I guess)
I think the problem with the implicit binding alternative is dealing with branches with moves:
|a1| { // `a1: !Copy`
drop(a1)
if flip_a_coin() {
yield x; // actually means `let (a1,) = yield x;` via implicit magic or whatever
// `a1` is valid here
}
// `a1` is not valid here
}
There are probably solutions to this, but apriori they seem incongruous normal rust syntax, or at least less teachable.
This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.