Idea: Existential lifetimes


#1

A very common pattern in certain kind of applications is like this: The program’s execution is separated into three phrases: In the first phrase we do some global configuration. In the second phrase we run the application logic, many times running an event loop or something. In a minimal third phrase we clean up the global configurations and exit.

It seems natural that people want to store the states their created in the first phrase into some global variables, and access all of them during the second phrase. However, any attempt to do so will quickly see one limitation of Rust: static variables have to be 'static, since there’s no "named global lifetime"s yet in rust.

So the idea is that we can invent some “existential lifetime” concept, make it act as “named global lifetime” and model such usages. The existential lifetime itself will be resolved as a region covering the whole phrase 2 code in main()(not really sure about the details).

This is just an idea. Not sure whether it’s feasible or the best solution. Just seeking for some comments.


#2

I can’t find it, but I know there was some proposal for "scoped 'static" previously.

But it seems the best way to handle this is to do so similarly to the minimal Rust runtime or “life before main”:

In the real main, do some global setup and promise (using unsafe and maybe static mut) that you have effectively 'static data. Then call the application main. Cleanup is after application main returns.

Unfortunately this is broken by the fact 'static allows unscoped thread spawning, so references could outlive the expiry of the application main. This means that teardown within the real main can’t put global state into an unsafe state.

Rustc handles this kind of problem by threading 'ctx through most of its interesting data structures and functions. There’s definitely design room for “named, scoped 'static replacements” (ambient lifetimes), but I don’t know what it would look like yet, or how much it would hurt from losing access to 'static manipulators.


#3

If you want to set global config at some point, then this can be done by wrapping it in OnceCell.

But I’d recommend to just forget that globals exist. The alternative is to pass configuration as arguments to functions that need it.

And instead of one huge config for everything, you can break it down to smaller configs for each area of functionality. This way configuration for each function is smaller, and you have fewer possible config combinations to think about. This is usually also easier to test in isolation.


#4

Or not. Why no store the configuration in a local variable? Then call the application function with that variable as an argument.

If the main application function is defined as a method on the configuration variable, it can all be done quite seamlessly (imho, the best selling point of methods), and that is actually the idiomatic way to go. If you need teardown, just impl Drop for YourConfig; plus you can / may need to make the main application’s function take ownership of the configuration (self by value). The code will be readable and robust to refactoring.

Here is an example of how it would go: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=407cc84981ee18344793c30a86321859


#5

How about turn your global immutable configuration into a Box and leak() it? Then you can just pass around shared static references at will.