【问题标题】:Initializing a Rust variable passed to async code such as tokio and hyper初始化传递给异步代码(例如 tokio 和 hyper)的 Rust 变量
【发布时间】:2020-02-12 12:41:24
【问题描述】:

我有一个无法在编译时计算的值。它需要在任何应用程序代码运行之前计算,然后它只会在应用程序的整个生命周期中被读取。它还需要传递给执行器,例如tokiohyper 处理程序。

我怎样才能安全地、惯用地创造这样的价值,而不会造成不必要的性能损失?

  • 如果我在main 中创建它并将其传递给hyper,它的寿命不够长。
  • 如果我使用lazy_static! 创建它,它只会在第一次访问时计算。如果无法计算,那么我也不想运行应用程序的其余部分。我宁愿知道我在启动应用程序时无法连接到数据库,而不是在客户端发出请求时。
  • 如果我将其设为static mut,则无法在安全代码中使用它。

理想情况下,我想做这样的事情:

#[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);
}

【问题讨论】:

标签: asynchronous rust rust-tokio hyper


【解决方案1】:

您可以泄漏内存,使引用具有'static 生命周期:

#[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);
}

【讨论】:

    【解决方案2】:

    如果我使用lazy_static! 创建它,它只会在第一次访问时计算。

    有一个特定的函数可以控制惰性静态变量何时为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();
    }
    

    【讨论】:

    • once_cell::Lazy 可以吗?
    • @Boiethios 我想应该是force
    【解决方案3】:

    您可以将数据包装在Arc 中,这样您的数据就可以被共享,并且它会一直存在,直到没有任何引用可供它使用:

    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

    【讨论】:

      最近更新 更多