(Quick notes: @nikomatsakis proposed something like this and I fleshed out portions of it with @tmandry. Eliza Weisman helped me flesh this out.)
I wrote up a rough proposal for a
Lifecycle trait, which is meant to support certain types of RAII guards in asynchronous contexts. Specifically, it exposes methods for a type to be notified if was yielded/resumed in a future or a generator. I have a draft which isn't complete, but is shareable: https://paper.dropbox.com/doc/Lifecycle-Trait--Asc4LYWRnMAQVIWxKRCNDP_3Ag-xW4HXDFuNGjeGdq9RkDAF.
I wanted to share this here before I posted a more fleshed-out proposal on internals.
@David Barsky :
It would be implemented as a fundamental trait, like Drop. This implies a blanket implementation for all types.
Drop is not implemented for all types. In fact, the
pin-project macro relies on this fact in order to to prevent user-written
Drop impls of certain annotated types.
Instead, the compiler has the notion of "drop glue", where types that are
String) nevertheless "do something" when dropped (invoking drop glue for fields)
Lifecycle is impemented for all types (e.g.
T: Lifecycle always holds), then this would need to rely on specialization in some way
Lifecycle to work in the same way as as
Drop, we we need to introduce a notion of
Lifecycle glue. This would require making
Lifecycle even more deeply integrated into the core language
@Aaron Hill oh, that's _really_ cool—I didn't know that. in that case,
Lifecycle should follow Drop's footsteps, or it is a "magic" trait like
Deref which is implemented on only _some_ types.
Did you mean
Drop instead of
that was a confusingly worded sentence :)
Deref is only magical in that it has syntactic sugar, and can be automatically invoked
what i'm trying to get to is that this trait should be minimal and opt-in.
Deref glue" doesn't exist
and ideally, would reduce how much work is needed to support to it.
Lifecycle shouldn't rely on specialization or negative bounds.
it's meant to be more of a hint to the compiler along the lines of: "if you see a type implementing a trait named
Lifecycle, do this codegen"
On the design level, I'm concerned about how implicit this is. From the perspective of an
async fn author, arbitrary code is being injected both before and after every
Drop, you look at the indentation of a variable to get a hint as to where it's drop glue will run (assuming it's not moved out of). With
Lifecycle, you essentially need to keep a mental stack of all locals, which you consult whenever you see a
I think the explicit approach of annotating the function makes things a lot more readable. I think composing futures is much easier to understand (analogous to the composition of functions) in terms of existing concepts. The
Lifecycle trait is in many ways a "new" concept that all users need to be aware of in order to understand the behavior of their code
Thanks for the design feedback—the "implicitness" is a concern for me as well, and I'm not 100% sold on the utility of this. I'd like to fully understand if some sort of task locals/structured concurrency could address the scoping of spans without language changes.
I realized that Node's Async Hooks is some prior work similar to this proposal: https://nodejs.org/api/async_hooks.html