I find it too magical to be necessary, but I can see how it might be useful. This can be achieved with a wrapper, but will then require you to wrap every future, which is not too convenient
It’s not about magic, it’s just not how the abstraction works.
Let’s have a concrete example, poem::web::Data utilizes opentelemetry::context::FutureExt which has the trait methods with_context() and with_current_context(). But where is the data/context actually stored? In the WithContext struct of course. Because there is no such a thing as pinning (meta)data to a trait.
Rust doesn’t do implicit transparent structs. The FutureExt trait methods return a wrapper struct WithContext which is where the data (T) and the context are stored. One method takes context directly from an argument, the other clones one from a thread local. Data can’t hang in thin air to be attached to a triat. This also shows that the abstraction you (presumably) were asking about is both possible and already exists.
Forcing a context on all T: Future types would make the abstraction non-zero-cost in any case.
You’re really hung up on the fact I said “pin metadata to a Future”. I didn’t mean that literally, but conceptually. The Future trait defines the API, and what I’m asking for is for that API to provide a mechanism to retrieve a context object that can be used to store arbitrary data. It’s almost there already. The Context that poll takes has a pubconstfnext(&mutself) -> &mut (dyn Any + 'static) method already (although I’m not thrilled by the type signature, I’d prefer it have an associated type or generic letting you define your own concrete extension type), it’s just that by being associated to Wake instead of Future it makes it impractical to use for storing and retrieving metadata when working with Futures.
There’s a few ways that the problem could be approached from. One option would be to add a new associated type to Future for an arbitrary context object. If you don’t need it you could always just set it to () same as is done for the Output type. There might be a clever way to re-use the task Context object that Wake has although I’m not sure how that would be done. Could also define a new standard trait similar to how FutureExt does things (maybe something like ContextualFuture), but that’s less useful for exactly the reason you pointed out, that Rust doesn’t do implicitly transparent structs.
You can stop repeating that traits don’t literally store data, everyone already knows that. Everyone knew that from the beginning. Repeating that again and again serves no purpose at all. Nobody is asking to literally store data on a trait.
Fundamentally the issue is that Future is a n+m model where you have n virtual threads executing on m OS threads. As such traditional solutions to associating data to OS threads like thread locals don’t work as there’s no guarantee that a given virtual thread will continue to execute on any given OS thread. Many languages have faced this exact problem and the solution that’s been arrived at in nearly every case is to add an associated execution context (in a variety of ways) that can be queried for from the runtime and used to store the equivalent of “thread local” data. That’s literally what I’m asking for, but in a way that’s part of the standard API so you don’t need to be tightly coupled to a specific async runtime. It is a problem that comes up repeatedly which is why things like FutureExt exist, but it should be part of the standard, not something ad hoc that everyone keeps having to solve on their own.
Other languages have ended up introducing it out of practical necessity, e.g. Go’s contexts, JS execution contexts. Pick your poison, although I expect Rust’s general minimal approach will leave it as extra parameters, Go-style.
Yes, and? It defines the API all the runtimes conform to.
Stating the obvious: a trait is type class you implement for types. It’s not something you can “pin metadata” (or any data) to.
It’s an API, if you add methods to it then the implementations will support that. That is in fact the entire point of a trait.
struct SomeFuture; impl Future for SomeFuture { // .... }where are you going to “pin metadata”?
I think, they mean:
pub trait Future { // .... fn put_metadata(...); fn get_metadata(...); }I find it too magical to be necessary, but I can see how it might be useful. This can be achieved with a wrapper, but will then require you to wrap every future, which is not too convenient
It’s not about magic, it’s just not how the abstraction works.
Let’s have a concrete example, poem::web::Data utilizes opentelemetry::context::FutureExt which has the trait methods
with_context()andwith_current_context(). But where is the data/context actually stored? In theWithContextstruct of course. Because there is no such a thing as pinning (meta)data to a trait.@[email protected] ^^
You do realize FutureExt is a trait right? That’s literally what I’m asking for just with it baked into Future instead of FutureExt.
FutureExttrait methods return a wrapper structWithContextwhich is where the data (T) and the context are stored. One method takes context directly from an argument, the other clones one from a thread local. Data can’t hang in thin air to be attached to a triat. This also shows that the abstraction you (presumably) were asking about is both possible and already exists.T: Futuretypes would make the abstraction non-zero-cost in any case.You’re really hung up on the fact I said “pin metadata to a Future”. I didn’t mean that literally, but conceptually. The
Futuretrait defines the API, and what I’m asking for is for that API to provide a mechanism to retrieve a context object that can be used to store arbitrary data. It’s almost there already. TheContextthat poll takes has apub const fn ext(&mut self) -> &mut (dyn Any + 'static)method already (although I’m not thrilled by the type signature, I’d prefer it have an associated type or generic letting you define your own concrete extension type), it’s just that by being associated toWakeinstead ofFutureit makes it impractical to use for storing and retrieving metadata when working withFutures.There’s a few ways that the problem could be approached from. One option would be to add a new associated type to
Futurefor an arbitrary context object. If you don’t need it you could always just set it to()same as is done for theOutputtype. There might be a clever way to re-use the taskContextobject thatWakehas although I’m not sure how that would be done. Could also define a new standard trait similar to howFutureExtdoes things (maybe something likeContextualFuture), but that’s less useful for exactly the reason you pointed out, that Rust doesn’t do implicitly transparent structs.You can stop repeating that traits don’t literally store data, everyone already knows that. Everyone knew that from the beginning. Repeating that again and again serves no purpose at all. Nobody is asking to literally store data on a trait.
Fundamentally the issue is that
Futureis a n+m model where you have n virtual threads executing on m OS threads. As such traditional solutions to associating data to OS threads like thread locals don’t work as there’s no guarantee that a given virtual thread will continue to execute on any given OS thread. Many languages have faced this exact problem and the solution that’s been arrived at in nearly every case is to add an associated execution context (in a variety of ways) that can be queried for from the runtime and used to store the equivalent of “thread local” data. That’s literally what I’m asking for, but in a way that’s part of the standard API so you don’t need to be tightly coupled to a specific async runtime. It is a problem that comes up repeatedly which is why things likeFutureExtexist, but it should be part of the standard, not something ad hoc that everyone keeps having to solve on their own.Other languages have ended up introducing it out of practical necessity, e.g. Go’s contexts, JS execution contexts. Pick your poison, although I expect Rust’s general minimal approach will leave it as extra parameters, Go-style.