Thanks for the detailed feedback @Matthias247.
Details
How does the compiler know which function destroys the object?
Through destructuring the type into its constituent parts (i.e. let Self { c1, c2 } = foo
). Most commonly, through a method that takes self
. No annotations are needed.
How do you get a !Leave
future out of an async fn
which doesn't take self
? Would it be based on any parameter?
Either drive it to completion locally or return the future back to the caller. Please give an example if I misunderstood your question.
What is the panic
behavior? This had more or less been pointed out in other questions, but it came up for me too.
Explained here. The discussions have shown we need to tweak Leave
or break it up.
General
Overall I'm feeling this is an attempt to generalize over a couple of challenges and doing more clever static analysis via traits. However I'm feeling like it might lean a bit too much into the "generalization" area, and might therefore be not very accessible to a large set of users.
There's a big difference between just "a new auto-trait" and "how invasive is the new trait". Unpin
turned out quite invasive, and this is problematic whereas Sized
is less invasive. I am only happy if we achieve minimal mental overhead, have sane defaults and actionable compiler errors.
The main one from the text I can find is that the extra scope for crossbeam would not be necessary - but I think this is a minor cosmetic issue [...]
The extra closure is needed because the desired logic cannot be safely expressed in Rust's type system (due to leaks being allowed). This is not jost a cosmetic limitiation, but a type system limitiation, which is why scoped tasks (in async) can't be implemented today. Given the trajectory of async, not having static borrowing across tasks is a very real issue, with problematic workarounds: "Arc
everywhere" are not zero-cost, causes boilerplate noise and has correctness pitfalls.
I'm a bit worried this adds another marker trait to the language.
Yes - don't worry, the "is it really worth it"-discussion is unavoidable - independent of which proposal gets widespread scrutiny. My goal here is making this proposal as good as possible before that. Note that nobody, not even me, wants to merge this into Rust at this point. That's why I started this thread.
I also share the concern that this might be hard to use within loops, iterators and other control flow.
Yes, we need to see how it "feels" in real world code. Fortunately, we do have some precedent - uninitialized variables is the spiritual inverse of this feature.
For the "unexpected async cancellation" challenge, there is the proposal of introducing async fn
s which run to completion.
Yes, and here's the link. This proposal deserves its own review. Very briefly, my perspective is:
- Some futures are state machines.
- Some state machines should not be dropped prior to reaching a terminal state.
- In non-panic cases, a state machine that is not driven to its terminal state correctly should be able to cause compiler errors (much like
must_use
does - but stronger), requiring the attention of the programmer.
This proposal assumes that this is a general problem which deserves a general solution. It is not clear to me that a new future type with an unsafe interface that needs a parallel eco-system of combinators is a less invasive change, but I'm not ruling it out either. I think more prototyping is needed, especially this proposal.