2

I have a value that cannot be computed at compile time. It needs to be computed before any of the app code runs, and then it will only be read throughout the lifetime of the app. It also needs to be passed around to executors such as tokio and hyper handlers.

How can I create such a value safely, idiomatically and without unneeded performance losses?

  • If I create it in main and pass it to hyper, it does not live long enough.
  • If I create it with lazy_static!, it only gets computed when it's first accessed. If it can't be computed, then I don't want to run the rest of the application either. I'd rather know I can't connect to the database when I start the application, not when a client makes a request.
  • If I make it a static mut, then I can't use it in safe code.

Ideally, I'd like to do something like:

#[tokio::main]
pub async fn main() {
    let db = init_db();

    // This uses a hyper server, passes db around
    // to tokio and hyper handlers, etc.
    run_app(&db);
}
rid
  • 61,078
  • 31
  • 152
  • 193
  • 3
    `lazy_static!` and access it in the beginning of `main` so that it gets computed early? – Jmb Feb 12 '20 at 13:13
  • @Jmb, that would work, but it seems a bit ugly. Not sure though if it's more performant than the Arc + RefCall or the leaking ideas. I will take performance over beauty in the end though. – rid Feb 12 '20 at 13:41
  • It appears that your question is answered by [How to share immutable configuration data with hyper request handlers?](https://stackoverflow.com/q/55606450/155423); [Shared mutable state in Hyper](https://stackoverflow.com/q/52974081/155423); [How do I structure a hyper HTTP app to handle shared state?](https://stackoverflow.com/q/51246069/155423); [How do I share a HashMap between Hyper handlers?](https://stackoverflow.com/q/38042592/155423); [Issue passing mutable Arc reference to hyper service_fn handler](https://stackoverflow.com/q/55164529/155423). – Shepmaster Feb 12 '20 at 13:53

3 Answers3

5

You can leak the memory, so that the reference has a 'static lifetime:

#[tokio::main]
pub async fn main() {
    let db = Box::leak(Box::new(init_db())) as &'static _;

    // This uses a hyper server, passes db around
    // to tokio and hyper handlers, etc.
    run_app(db);
}
Boiethios
  • 38,438
  • 19
  • 134
  • 183
4

If I create it with lazy_static!, it only gets computed when it's first accessed.

There's a specific function to control when a lazy static variable is initialized:

use lazy_static::lazy_static; // 1.4.0

lazy_static! {
    static ref THING: String = String::from("a");
}

#[tokio::main]
pub async fn main() {
    lazy_static::initialize(&THING);
    run_app();
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
2

You can wrap the data in a Arc, so your data can be shared and it will live until no references are left for it:

use tokio::prelude::*;
use tokio;
use std::sync::Arc;

async fn init_db() -> Arc<String> {
    Arc::new("Foo".to_string())
}

async fn run_app(data: Arc<String>) {
    for _ in 0..10 {
        println!("{}", data);
    }
}

#[tokio::main]
pub async fn main() {
    let db = init_db().await;

    // This uses a hyper server, passes db around
    // to tokio and hyper handlers, etc.
    run_app(db).await;
}

Playground version

Netwave
  • 40,134
  • 6
  • 50
  • 93