Rust 的 Lazy init(OnceCell crate)并非在所有地方都有效

Foi*_*ard 0 asynchronous lazy-loading rust discord

我正在用Serenity用 Rust 编写一个不和谐的机器人。为了填充一些嵌入标头,我需要与机器人相关的信息(用户和所有者,以下称为BOT),并且我尝试使用Once_cellLazy板条箱中的结构来实现这一点。

问题是这些BOT数据只能使用 Serenity 的 Http 模块的异步函数来查询,而我正在努力使其工作。

我设置了一些斜杠命令,每个命令都有自己的处理函数。当我BOT从这些处理程序函数之一取消引用(这就是应该评估它(仅一次),对吧?)时,我的Lazy闭包在它遇到的第一个函数上阻塞.await

另一方面,当我BOTmain(tokio async)取消引用时,它才起作用。

因此,以下代码在从特定斜杠命令处理程序 (BoxedFuture) 取消引用时打印“0,1,2,3” BOTmain但仅打印“0,1”:

use once_cell::sync::Lazy;
use serenity::{http::Http, model::user::User};

pub struct Bot {
    pub user: User,
    pub owner: User,
}

pub static BOT: Lazy<Bot> = Lazy::new(|| {
    println!("0");
    let http_client = Http::new(env!("DISCORD_BOT_TOKEN"));

    futures::executor::block_on(async {
        println!("1");
        let user = http_client
            .get_current_user()
            .await
            .expect("An HTTP client error occured")
            .into();

        println!("2");
        let owner = http_client
            .get_current_application_info()
            .await
            .expect("An HTTP client error occured")
            .owner;

        println!("3");
        Bot { user, owner }
    })
});
Run Code Online (Sandbox Code Playgroud)

谁能解释这是为什么吗?

Cha*_*man 6

永远不应该在异步运行时内阻塞。永远不能。

您可以使用tokio::sync::OnceCell代替once_cell

pub async fn get_bot() -> &'static Bot {
    static BOT: tokio::sync::OnceCell<Bot> = tokio::sync::OnceCell::const_new();
    BOT.get_or_init(|| async {
        println!("0");
        let http_client = Http::new(env!("DISCORD_BOT_TOKEN"));

        println!("1");
        let user = http_client
            .get_current_user()
            .await
            .expect("An HTTP client error occured")
            .into();

        println!("2");
        let owner = http_client
            .get_current_application_info()
            .await
            .expect("An HTTP client error occured")
            .owner;

        println!("3");
        Bot { user, owner }
    })
    .await
}
Run Code Online (Sandbox Code Playgroud)