如何将lazy_static与async/await初始化器一起使用?

Moh*_*med 22 rust async-await

我使用mongodbtokio作为我的异步运行时。

Client我想全局初始化 MongoDB,所以我使用了惰性静态箱。问题是客户端是异步连接的,lazy_static!不支持async关键字:

use mongodb::Client;

async {
    let client = Client::with_uri_str(&env_var("MONGO_URL")).await.unwrap();
}
Run Code Online (Sandbox Code Playgroud)

那么我该如何初始化呢client

有关的:

kmd*_*eko 29

如果您使用新的运行时,并且首先在现有运行时的上下文中使用惰性静态,如下例所示:

use lazy_static::lazy_static;
use mongodb::Client;

lazy_static! {
    static ref CLIENT: Client = {
        tokio::runtime::Runtime::new().unwrap().block_on(async {
            let uri = std::env::var("MONGO_URL").unwrap();
            let client = Client::with_uri_str(&uri).await.unwrap();

            client
        })
    };
}

#[tokio::main]
async fn main() {
    let _db = CLIENT.database("local");
}
Run Code Online (Sandbox Code Playgroud)

您将收到提到的错误:

use lazy_static::lazy_static;
use mongodb::Client;

lazy_static! {
    static ref CLIENT: Client = {
        tokio::runtime::Runtime::new().unwrap().block_on(async {
            let uri = std::env::var("MONGO_URL").unwrap();
            let client = Client::with_uri_str(&uri).await.unwrap();

            client
        })
    };
}

#[tokio::main]
async fn main() {
    let _db = CLIENT.database("local");
}
Run Code Online (Sandbox Code Playgroud)

可以通过使用不同的运行时(futuresvs tokiovs async-std)来规避这一点,但这并不理想,因为它仍然会阻塞底层运行时。


解决这个问题的一个相对直接的方法不是尝试懒惰地执行并立即在main. 这样您就可以直接使用异步运行时,而不必担心在其他地方需要异步运行时:

use mongodb::Client;
use once_cell::sync::OnceCell;

static CLIENT: OnceCell<Client> = OnceCell::new();

#[tokio::main]
async fn main() {
    let uri = std::env::var("MONGO_URL").unwrap();
    let client = Client::with_uri_str(&uri).await.unwrap();
    CLIENT.set(client).unwrap();

    let _db = CLIENT.get().unwrap().database("local");
}
Run Code Online (Sandbox Code Playgroud)

如果需要的话,可以通过OnceCell(如上所示)或类似的方式来完成。更新:现在也在标准库中做同样的事情。RwLockOnceLock


对于您想要实现的目标,最直接的答案是使用async_once板条箱,这使得它使用接收器的运行时来驱动异步函数。

use async_once::AsyncOnce;
use lazy_static::lazy_static;
use mongodb::Client;

lazy_static! {
    static ref CLIENT: AsyncOnce<Client> = AsyncOnce::new(async {
        let uri = std::env::var("MONGO_URL").unwrap();
        let client = Client::with_uri_str(&uri).await.unwrap();

        client
    });
}

#[tokio::main]
async fn main() {
    let _db = CLIENT.get().await.database("local");
}
Run Code Online (Sandbox Code Playgroud)

这假设客户端的所有或几乎所有使用都将在异步上下文中。

或者您可以使用OnceCellfrom tokio,它类似于OnceCell/ ,OnceLock但它的初始化程序async类似于AsyncOnce这样您可以在辅助函数中main或通过辅助函数单独初始化它:

use mongodb::Client;
use tokio::sync::OnceCell;

static CLIENT: OnceCell<Client> = OnceCell::const_new();

async fn get_client() -> &'static Client {
    CLIENT.get_or_init(|| async {
        let uri = std::env::var("MONGO_URL").unwrap();
        let client = Client::with_uri_str(&uri).await.unwrap();

        client
    })
    .await
}

#[tokio::main]
async fn main() {
    let _db = get_client().await.database("local");
}
Run Code Online (Sandbox Code Playgroud)