this post was submitted on 20 Mar 2026
24 points (100.0% liked)

Rust Programming

9201 readers
19 users here now

founded 7 years ago
MODERATORS
you are viewing a single comment's thread
view the rest of the comments
[–] orclev@lemmy.world 1 points 1 day ago

This is fully runtime/platform dependant and not true in many cases.

Yes, but this is also fundamentally the problem. If you don't want to tightly couple yourself to a particular runtime you can't make any assumptions at all about the underlying runtime and therefore need to write things in such a way that they work equally well across all of them. In the cases where a runtime is single-threaded you could easily achieve the desired result by simply stuffing the context into a thread-local, but that would then be an implementation detail of the runtime and you therefore couldn't assume that.

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.

Maybe a concrete example would help. It is often the case in a REST-ful web service that you want to execute a request while retaining a set of headers from the request. Some of these headers are useful to include in log messages during request processing. Some of them need to be passed along to other services when those calls are executed. Some of them can be used to alter logic during the request processing. Those are also not always exclusively one or the other, it's very often that a header might be used E.G. in logging as well as passed along to outbound requests.

There are a number of ways you could approach this problem. One option would be to simply pass the headers around explicitly as function arguments. In order to keep the number of arguments to a reasonable quantity you probably want to stuff all the headers into some kind of container, either a simple map or a more type safe context object. Regardless though this can be very tedious as you end up needing to pass this same argument to nearly every function in your app.

In order to keep things tidy you're going to want to find an out of band way to pass that around. In a traditional model, you could stuff it into a thread-local, and indeed many application do just that. That doesn't work though with Rusts async model typically, for the reason I had outlined previously.

The solution the opentelemetry crate came up with was to store the context in a thread-local, while also wrapping arbitrary Future instances in a wrapper that overwrites the current threads thread-local with one stored in the wrapper when it's polled, which works but is kind of a brute force approach as well as being slightly fragile. Instead of having to come up with such an approach it would be nice if the ability to attach and maintain a context object was part of the async API so that each runtime would then be able to expose a mechanism that was appropriate to its own execution implementation.