3 ms·
I think (but I cannot find a reference to now) one of the proposed syntaxes was that awaits were immediate and implicit in async functions, and that if you didn
by deathanatos 7y ago
I think (but I cannot find a reference to now) one of the proposed syntaxes was that awaits were immediate and implicit in async functions, and that if you didn't want that, you could opt-out and get futures normally. Loosely,
async fn foo() {
let result = blocking_req(); // awaiting immediately here.
async {
let future = blocking_req(); // not awaited.
// can do more complex stuff w/ the future
}
}
IDK what happened to it, and it is a bit more complex IMO than what the Rust folks went with. However, I don't think it helps your gather case.
For gather, I think the problem w/ your proposed
return alpha + beta + gamma
is when does that happen? If I do
let some = alpha + beta;
// < ... code ... >
let more = some + gamma;
what happens / when are things evaluated?
At any rate, with the syntax as it is, I think your gather example is essentially¹:
alpha.join3(beta, gamma).await
or
Future::join3(alpha, beta, gamma).await
(Perhaps minus the `.await`, depending on when you need/want the result.)
There's no operator overloading, which I'm fine w/, as there is a need to choose between, e.g., select or join.
¹but I'm new w/ the async stuff, so if I'm wrong I'm assuming that Cunningham's Law will kick in and someone will correct me.
- Rusky 7y agoHere's the proposal: https://internals.rust-lang.org/t/explicit-future-construction-implicit-await/7344 https://internals.rust-lang.org/t/explicit-future-constructi... One of the goals behind it was to align async control flow with sync/thread-based control flow, to make it more intuitively obvious when things would run concurrently and when they would not. That is, calling a function, even if async, would immediately run it to completion, while wrapping it up in a closure or async block that gets spawned would run it concurrently. This is especially relevant to Rust, which departs from other implementations of async/await in that merely constructing a future is insufficient to start it running, which is why you need that `join3` call to explicitly introduce concurrency. Under the "implicit await" proposal, your first example would look like this: async fn foo() { let result = blocking_req(); // await immediately let future = async { blocking_req() }; // think of async{} like a lambda function // can do more complex stuff with the future, like: // future.await() // Future::join(future, other_future) // tokio::spawn(future) } The thread-based equivalent looks very similar: fn foo() { let result = blocking_req(); // block the current thread immediately let closure = || { blocking_req() }; // don't run this yet // can do more complex stuff with the closure, like: // closure() // rayon::join(closure, other_closure) // thread::spawn(closure) }
- ben509 7y agoThe async block syntax would be nice, and probably far more explicit. I think with the `return alpha + beta + gamma` the idea is that within a scope, everything is done as late as possible, but you can't leave a scope without all results. Thus, if the code is expanded as: alpha = run_a() beta = run_b() gamma = run_c() a_plus_b = plus(alpha, beta) sum = plus(a_plus_b, gamma) return sum In that case, the compiler has to determine a partial ordering: a_plus_b <- run_a, run_b sum <- a_plus_b, run_c Thus it can generate: await gather(run_a, run_b, run_c) await plus(alpha, beta) await plus(a_plus_b, gamma) And presumably you can detect pure subroutine / primitive invocations. And there would be other hard points where tasks must be fully awaited; e.g. loops wouldn't be able to leak tasks.