• BB_C@programming.dev
    link
    fedilink
    arrow-up
    1
    ·
    7 days ago

    Stating the obvious: a trait is type class you implement for types. It’s not something you can “pin metadata” (or any data) to.

    • orclev@lemmy.world
      link
      fedilink
      arrow-up
      3
      ·
      7 days ago

      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.

      • BB_C@programming.dev
        link
        fedilink
        arrow-up
        1
        ·
        7 days ago
        struct SomeFuture;
        
        impl Future for SomeFuture {
          // ....
        }
        
        

        where are you going to “pin metadata”?

        • lad@programming.dev
          link
          fedilink
          English
          arrow-up
          3
          ·
          7 days ago

          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

          • devnev@lemmy.dbzer0.com
            link
            fedilink
            arrow-up
            3
            ·
            7 days ago

            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.

            • orclev@lemmy.world
              link
              fedilink
              arrow-up
              3
              ·
              edit-2
              7 days ago

              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.

              • BB_C@programming.dev
                link
                fedilink
                arrow-up
                1
                ·
                7 days ago
                • 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.
                • orclev@lemmy.world
                  link
                  fedilink
                  arrow-up
                  3
                  ·
                  7 days ago

                  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 pub 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 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.

                  • BB_C@programming.dev
                    link
                    fedilink
                    arrow-up
                    1
                    ·
                    6 days ago

                    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.

                    This is fully runtime/platform dependant and not true in many cases. Not only strictly single-thread runtimes exist, but async is already used in places like the embedded world where there is no OS for there to be OS threads, single or otherwise.

                    It is for this reason that the extra trait bounds on the implicit impl Future return type in async functions have caused a lot of trouble (implicit Send, …etc). If for nothing else, adding context to the equation would make that situation more complex.

                    In any case, attaching “metadata” to T: Future data (instead of the runtime or some other global context) doesn’t make any sense in my head, as one would be "await"ing around and the concrete return type is often hidden behind that impl Future. And if the data is really special and needs to be attached to T: Future, then it’s (conceptually) just data that is a part of T, not Future.