# Pre-RFC: Contextual paramaters

**URL:** <https://internals.rust-lang.org/t/pre-rfc-contextual-paramaters/21849>\
**Category:** compiler\
**Created:** [November 10, 2024, 9:51pm UTC](https://internals.rust-lang.org/t/pre-rfc-contextual-paramaters/21849 "2024-11-10T21:51:43Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![binarycat](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/binarycat/32/12210_2.png) [@binarycat](https://internals.rust-lang.org/u/binarycat)\
**Post date:** [November 10, 2024, 9:51pm UTC](https://internals.rust-lang.org/t/pre-rfc-contextual-paramaters/21849/1 "2024-11-10T21:51:43Z")

</div>

## Problem Statement

Within certain codebases, there is certain state that needs to be made available to almost every function. Examples are loggers, async runtimes, and object capabilities.

Frequently, global variables are used for this purpouse, but this can cause problems, such as the common problem of awaiting a future that expects a `tokio` runtime without first spawning such a runtime.

## Proposal

Add a new `context` [binding mode](https://doc.rust-lang.org/reference/patterns.html#binding-modes). This can appear on any identifier pattern, but has additional meaning when used on a function parameter.

When a function parameter is annotated with `context`, it is a _contextual parameter_. contextual parameters must come after all other non-contextual parameters. It is an error for a function to have two contextual parameters of the same type.

When a function with contextual parameters is called, some or all of those parameters may be ommited. if they are, the compiler searches the scope of the calling function for a binding of the correct type that is annotated with `context`\[1\], and uses those bindings as the argument\[2\].

### Example

```rust
fn do_thing(context rt: Executor<'_>) {
   // <run whatever async tasks>
}

fn main() {
  // note that `context` comes after all other binding modes
  let mut k#context executor = smol::Executor::new();
  do_thing();
}

```

* * *

1. This may be a contextual parameter, or any other binding 

2. it recommended that `context` bindings implement `Copy`, as otherwise they will be moved

---

<div class="post-metadata">

**Author:** ![jhpratt](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/jhpratt/32/11640_2.png) [@jhpratt](https://internals.rust-lang.org/u/jhpratt)\
**Post date:** [November 11, 2024, 1:43am UTC](https://internals.rust-lang.org/t/pre-rfc-contextual-paramaters/21849/2 "2024-11-11T01:43:50Z")

</div>

See [Tracking Issue for externally implementable items · Issue #125418 · rust-lang/rust · GitHub](https://github.com/rust-lang/rust/issues/125418), which is the most recent proposal to do this (and more flexible!)

---

<div class="post-metadata">

**Author:** ![binarycat](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/binarycat/32/12210_2.png) [@binarycat](https://internals.rust-lang.org/u/binarycat)\
**Post date:** [November 11, 2024, 2:02am UTC](https://internals.rust-lang.org/t/pre-rfc-contextual-paramaters/21849/3 "2024-11-11T02:02:45Z")

</div>

that's... completely different? that seems to basically be an equivelent to C's `weak` linkage, where I am proposing something like scala's [context parameters](https://docs.scala-lang.org/scala3/book/ca-context-parameters.html).

are you confusing _bindings_ with _items_?

do you think they're the same because they can both be used to customize logging behavior? that's like saying mut statics are the same thing as the builder pattern.

one is process-global and requires integration with linkers, what i'm suggesting would be local to a specific call stack (thread, task, etc.) and would be purely syntactic sugar.

---

<div class="post-metadata">

**Author:** ![jrose](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/jrose/32/9591_2.png) [@jrose](https://internals.rust-lang.org/u/jrose)\
**Post date:** [November 11, 2024, 2:35am UTC](https://internals.rust-lang.org/t/pre-rfc-contextual-paramaters/21849/4 "2024-11-11T02:35:34Z")

</div>

We do have one of these today, sort of: `#[track_caller]`. That’s not quite the same, of course, since in practice it’s guaranteed to have a compiler-generated source, but it is an example of an implicit parameter at the ABI level.

---

<div class="post-metadata">

**Author:** ![jhpratt](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/jhpratt/32/11640_2.png) [@jhpratt](https://internals.rust-lang.org/u/jhpratt)\
**Post date:** [November 11, 2024, 4:24am UTC](https://internals.rust-lang.org/t/pre-rfc-contextual-paramaters/21849/5 "2024-11-11T04:24:45Z")

</div>

Ah, you want it for a specific call stack. I (wrongly) assumed your intent given the problem statement.

---

<div class="post-metadata">

**Author:** ![josh](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/josh/32/5934_2.png) [@josh](https://internals.rust-lang.org/u/josh)\
**Post date:** [November 11, 2024, 5:12am UTC](https://internals.rust-lang.org/t/pre-rfc-contextual-paramaters/21849/6 "2024-11-11T05:12:51Z")

</div>

You may want to look at [this post about "contexts and capabilities"](https://tmandry.gitlab.io/blog/posts/2021-12-21-context-capabilities/) from a few years ago. There's still an ongoing desire to consider something like that.

---

<div class="post-metadata">

**Author:** ![DasLixou](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/daslixou/32/12132_2.png) [@DasLixou](https://internals.rust-lang.org/u/DasLixou)\
**Post date:** [November 11, 2024, 5:29pm UTC](https://internals.rust-lang.org/t/pre-rfc-contextual-paramaters/21849/7 "2024-11-11T17:29:35Z")

</div>

It seems to have a similar intent as [Pre-pre-RFC/Working Prototype: Borrow-Aware Automated Context Passing - #2 by Radbuglet](https://internals.rust-lang.org/t/pre-pre-rfc-working-prototype-borrow-aware-automated-context-passing/21279/2) Just posting to link those

---

<div class="post-metadata">

**Author:** ![mathstuf](https://avatars.discourse-cdn.com/v4/letter/m/958977/32.png) [@mathstuf](https://internals.rust-lang.org/u/mathstuf)\
**Post date:** [November 11, 2024, 9:48pm UTC](https://internals.rust-lang.org/t/pre-rfc-contextual-paramaters/21849/8 "2024-11-11T21:48:54Z")

</div>

> [@binarycat](#):
>
> It is an error for a function to have two contextual parameters of the same type.

Presumably you mean "two contextual variables of the same type in a given scope" as otherwise the automatic selection is ambiguous if there are two local context variables of the same type. There are also issues if the context parameter is something of `&dyn ContextTrait` or the like and multiple implementers are in scope. Let's say we have a context wrapper that logs or something:

```rust
struct LogContext<C>(Context);
impl<C> ContextTrait for LogContext<C> where C: ContextTrait { /* impl */ }

fn uses_context_with_logging(context ctx: &dyn ContextTrait) {
    let k#context wrap = LogContext(ctx);
    use_ctx(); // Does this get `ctx` or `wrap` if it wants a `&dyn ContextTrait`?
}

```

---

<div class="post-metadata">

**Author:** ![matthieum](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/matthieum/32/4023_2.png) [@matthieum](https://internals.rust-lang.org/u/matthieum)\
**Post date:** [November 15, 2024, 6:06pm UTC](https://internals.rust-lang.org/t/pre-rfc-contextual-paramaters/21849/9 "2024-11-15T18:06:21Z")

</div>

> [@binarycat](#):
>
> Examples are loggers, async runtimes, and object capabilities.

I am not sure that loggers / reporting / telemetry are a good example, here.

I think it's important to make a distinction between:

- Dev Ops: logging, reporting, telemetry, ...
- Function: async runtime, object capabilities, ...

The difference between the former and the latter being that the former should NOT affect the function of the library/application, and is purely of use for the developers/operators. Or otherwise said, if one where to deactivate all logging, reporting, telemetry, etc... either at compile-time or run-time, the user should not experience any difference (beyond performance fluctuation, perhaps).

> [@binarycat](#):
>
> Frequently, global variables are used for this purpose, but this can cause problems, such as the common problem of awaiting a future that expects a `tokio` runtime without first spawning such a runtime.

Is that such a problem in practice?

If you develop a tokio application, you're likely to have `#[tokio::main]` on `main`, and thus all your code will be executed within the context of a runtime.

I think this argument would be better off mentioning the importance of making implicit dependencies _explicit_, in a way that the compiler can machine-verified them, rather than as just a comment in the documentation.

* * *

## Weaknesses of context parameters

Which really should be mentioned in the RFC.

### Plumbing Pains

One advantage of implicit (global) parameter passing is that the _caller_ of the thingy need not be aware of the capabilities required by the callee.

This is all the more important for _logging_ for example. Imagine if each time you would want to add a log statement, you would first have to pass the logger through 4 or 5 callers, and then modify all the call-sites of _those_ callers since _they_ now require the logger argument _too_.

This is _pure_ pain, and why I strongly believe that using context parameters for logging is a mistake. The ergonomics are just terrible.

### Borrowing Pains

It should be noted that depending on the framework, there may not be any borrowing issue when using a global: if there's no re-entrancy path -- ie if no user-code is executed while borrowing the global -- then the global can be safely borrowed mutably.

This means that the global can pass through _agnostic 3rd-party code_ without issue.

If using context parameters, however, unless the 3rd-party code which invokes the function wishing to use the context parameter actually passes them -- the exact same types, too -- then the only way to access this parameter is to "bundle it" in the callback. Very quickly this'll require reference-counting, which itself will require some cell, ... this leads to both a memory overhead, and possibly a performance overhead, for example for `RefCell` and `Mutex`.

In this sense, a global is _more_ ergonomic...

---

<div class="post-metadata">

**Author:** ![epage](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/epage/32/3171_2.png) [@epage](https://internals.rust-lang.org/u/epage)\
**Post date:** [November 15, 2024, 6:46pm UTC](https://internals.rust-lang.org/t/pre-rfc-contextual-paramaters/21849/10 "2024-11-15T18:46:54Z")

</div>

There are a couple of RFCs like [[RFC] externally implementable functions by m-ou-se · Pull Request #3632 · rust-lang/rfcs · GitHub](https://github.com/rust-lang/rfcs/pull/3632) that could replace the need for Context parameters for some use cases, like logging, assuming you don't need to supplant one of these within a given scope which is the big benefit for Context.

---

<div class="post-metadata">

**Author:** ![binarycat](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/binarycat/32/12210_2.png) [@binarycat](https://internals.rust-lang.org/u/binarycat)\
**Post date:** [November 15, 2024, 8:48pm UTC](https://internals.rust-lang.org/t/pre-rfc-contextual-paramaters/21849/11 "2024-11-15T20:48:23Z")

</div>

> [@matthieum](#):
>
> Is that such a problem in practice?
> 
> If you develop a tokio application, you're likely to have `#[tokio::main]` on `main`, and thus all your code will be executed within the context of a runtime.

It's not a problem for applications, but it _is_ a problem for libraries, and it part of the reason why alternative async runtimes struggle so much. just looking at the documentation of [`reqwest`](https://docs.rs/reqwest/0.12.9/reqwest/), can you tell me if it requires a tokio runtime? I honestly have no idea what the answer is.

---

<div class="post-metadata">

**Author:** ![parasyte](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/parasyte/32/8925_2.png) [@parasyte](https://internals.rust-lang.org/u/parasyte)\
**Post date:** [November 15, 2024, 11:45pm UTC](https://internals.rust-lang.org/t/pre-rfc-contextual-paramaters/21849/12 "2024-11-15T23:45:12Z")

</div>

> [@binarycat](#):
>
> just looking at the documentation of [`reqwest`](https://docs.rs/reqwest/0.12.9/reqwest/), can you tell me if it requires a tokio runtime?

Yes.

 ![Screenshot 2024-11-15 at 3.33.28 PM](https://us1.discourse-cdn.com/flex002/uploads/rustlang/original/2X/0/066c653b81934bb56f91d6652997a30cc81e3114.png)

Not trying to be critical of the concern. It might be ideal to make global contexts apparent in the API surface area. But some of those details can be inferred through other means, like the dependency tree in this case.

I'm sympathetic to some of the example use cases in tmandry's article. Particularly arena allocator, string interner, wasmtime fuel, etc. Some others are less concerning: global logger, global metrics, global async runtime. These global contextual items usually don't have a good reason to change \[1\] at runtime. "Set it and forget" is good enough.

* * *

1. Or be "context sensitive", as it were.

---

<div class="post-metadata">

**Author:** ![binarycat](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/binarycat/32/12210_2.png) [@binarycat](https://internals.rust-lang.org/u/binarycat)\
**Post date:** [November 15, 2024, 11:57pm UTC](https://internals.rust-lang.org/t/pre-rfc-contextual-paramaters/21849/13 "2024-11-15T23:57:24Z")

</div>

Merely depending on tokio at compile time does not mean something depends on a tokio runtime, if you only use tokio for its traits, your code will be portable across runtimes. However, most functions in `tokio::task`\[1\] require a tokio runtime.

* * *

1. which reqwest happens to use

---

<div class="post-metadata">

**Author:** ![parasyte](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/parasyte/32/8925_2.png) [@parasyte](https://internals.rust-lang.org/u/parasyte)\
**Post date:** [November 16, 2024, 12:24am UTC](https://internals.rust-lang.org/t/pre-rfc-contextual-paramaters/21849/14 "2024-11-16T00:24:20Z")

</div>

True, but niche. Do you have a better example that illustrates the point?

---

<div class="post-metadata">

**Author:** ![binarycat](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/binarycat/32/12210_2.png) [@binarycat](https://internals.rust-lang.org/u/binarycat)\
**Post date:** [November 16, 2024, 1:07am UTC](https://internals.rust-lang.org/t/pre-rfc-contextual-paramaters/21849/15 "2024-11-16T01:07:08Z")

</div>

> [@parasyte](#):
>
> True, but niche.

how is an issue that effects all of async rust niche?

---

<div class="post-metadata">

**Author:** ![parasyte](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/parasyte/32/8925_2.png) [@parasyte](https://internals.rust-lang.org/u/parasyte)\
**Post date:** [November 16, 2024, 1:12am UTC](https://internals.rust-lang.org/t/pre-rfc-contextual-paramaters/21849/16 "2024-11-16T01:12:01Z")

</div>

Depending on `tokio` for its traits is niche, and has little to do with contextual parameters. I am asking if you have a better example than `reqwest` for documentation that doesn't tell you if the API depends on the runtime or not.

---

<div class="post-metadata">

**Author:** ![2e71828](https://avatars.discourse-cdn.com/v4/letter/2/3e96dc/32.png) [@2e71828](https://internals.rust-lang.org/u/2e71828)\
**Post date:** [November 16, 2024, 5:19am UTC](https://internals.rust-lang.org/t/pre-rfc-contextual-paramaters/21849/17 "2024-11-16T05:19:51Z")

</div>

In one of my hobby projects, I have two `async` runtimes active concurrently: One is single-threaded to run the display and the other is multi-threaded for long-running background tasks. Any `Future` that interacts with the graphics card can only run on the former, but as far as I can tell there isn’t currently a way to enforce this at compile-time.

---

<div class="post-metadata">

**Author:** ![Nemo157](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/nemo157/32/11585_2.png) [@Nemo157](https://internals.rust-lang.org/u/Nemo157)\
**Post date:** [November 16, 2024, 8:32am UTC](https://internals.rust-lang.org/t/pre-rfc-contextual-paramaters/21849/18 "2024-11-16T08:32:57Z")

</div>

`hyper` is such a crate, it uses tokio’s io traits and allows you to provide your own executor, timer etc. so it can run on a non-tokio runtime.

---

<div class="post-metadata">

**Author:** ![josh](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/josh/32/5934_2.png) [@josh](https://internals.rust-lang.org/u/josh)\
**Post date:** [November 16, 2024, 9:01am UTC](https://internals.rust-lang.org/t/pre-rfc-contextual-paramaters/21849/19 "2024-11-16T09:01:57Z")

</div>

> [@matthieum](#):
>
> If you develop a tokio application, you're likely to have `#[tokio::main]` on `main`

There's a world of async out there that doesn't use tokio, and it's very easy to end up being surprised by a library that expects a tokio runtime without being a tokio application.

Also, even if you're using tokio, it's still quite possible to have a thread started with `std::thread::spawn` that isn't running a runtime.

---

<div class="post-metadata">

**Author:** ![matthieum](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/matthieum/32/4023_2.png) [@matthieum](https://internals.rust-lang.org/u/matthieum)\
**Post date:** [November 16, 2024, 11:42am UTC](https://internals.rust-lang.org/t/pre-rfc-contextual-paramaters/21849/20 "2024-11-16T11:42:03Z")

</div>

> [@josh](#):
>
> Also, even if you're using tokio, it's still quite possible to have a thread started with `std::thread::spawn` that isn't running a runtime.

That's a good one. I always run single-threaded, so it hadn't occurred to me.

I wonder if this should be considered a "hole" in tokio (or any runtime, really). Perhaps there should be a way to tell tokio that any "orphan" thread should automatically join "this" multi-threaded runtime group unless otherwise overridden.

With that said, I do agree that explicitness of which runtime to use on which thread is useful, and that function parameters are a good way to make such requirements explicit.

I do wonder whether _context_ parameters are the best way, though. Especially the requirement to plumb them through all intermediate layers.

An alternative would be:

1. Allow getting a handle to the current tokio runtime via a `runtime::get_handle` function, which reads a global/thread-local variable. And its sister API, `runtime::get_handle_or`, which executes the callback to create the runtime if it doesn't already exists before returning a handle to it..
2. Pass that handle at API boundaries.

It makes the library/module dependency explicit, allows a library to easily provide support for alternative runtimes (notably by asking for a trait object, with the trait implemented for multiple runtimes), doesn't require plumbing all the way through, and allows the caller to realize that maybe they should spawn a runtime.

No language change necessary; it's a pure library concern.

[Next page](https://internals.rust-lang.org/t/pre-rfc-contextual-paramaters/21849.md?page=2)
