Re:Await in Rust
by naasking on Monday August 12, 2019 @01:06PM
(#20677419)
It sounds like you're suggesting programming in explicit CPS/callback style, which is doable, but doesn't match the goal of the initial post which was to program in direct style and let the callee determine whether it should execute async or sync.
[ Parent
| on HN ]
Oh, not at all, I want the compiler to do the CPS transform for me (same as for yield/await) and then hand me the incoming continuation as a first class lexically scoped parameter.
For example: convert an internal iterator to an external one (in pseudo C++, yes, yes, I know):
// This is a bog standard template function. Cont can be anything
// here as long as it is callable. It literally does the same thing
// as for_each
template<class Cont, class Iter>
Cont inside_out(Iter begin, Iter end, Cont yield){
// as long as yield lifetime is confined to the scope of
// inside_out; when Cont
// ends up being some compiler internal (possibly unique) magic
// type it should be possible to CPS convert both inside_out and
// for_each. Both rust and C++ monomorphize templates anyway so
// instantiating dedicated CPS variant is not a problem.
std::for_each(begin, end, [&](auto&& x) { yield(x); });
// for various reasons we need to return the continuation here. A
// proper language would tail call to it.
return yield;
}
...
// this is basically call/cc. It uses magic to get the current
// continuation, then invokes its first argument with it and the
// remaining arguments. The current function need to be
// Note: template functions are not first class in
// C++, but we take some liberty here.
auto cont = continuationify(inside_out, my_vec.begin(), y_vec.end());
while(not cont.done())
std::cout << cont();
if inside_out was not templated, but instead Cont was type erased (a trait object in rust parlance), then continuatinify would need to allocate a dedicated stack and fall back to good old userspace context switching (the compiler is welcome to pierce through the type erasure layer and optimize anyway, but it would not be a guaranteed optimization).Rust is close enough to C++ that it would work in the same way. I've been planning to write a paper for the C++ committee to add fire to the current coroutine controversy and proposing something like the above (which would unify stackless and stackful coroutines).
Re:Await in Rust
by a1369209993 on Monday August 12, 2019 @04:48PM
(#20679839)
I think they're suggesting programming in implicit CPS/callback style. Eg:
int bar(int i) { return i+1; }
/* bar takes a implicit return address in [esp+0] */
noreturn foo((*ra)(int),int x)
{
/* where (*)(RetType) is a return address */
int y = bar(x); /* x86 call pushes eip */
ra return bar(y); /* pseudo-tail-call (push ra) */
}
int quux(int x)
{
(*ret)(int); ret = (return); /* get implicit RA */
foo(ret,x);
} // unreachable
You'd also need to adjust the stack and/or frame pointer though, which is probably going to be the hard part.