You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust中等待异步函数返回的最简循环实现方法

问题描述

我正在编写一款国际象棋程序,我的Game类包含异步函数play,该函数会在游戏结束时返回一个Winner枚举值(代表和棋或某一方获胜)。我希望在main函数中调用game.play,并重复执行game.render(),直到game.play返回Winner枚举值后停止循环。我查阅了文档,但仍不理解为何轮询Future时需要Pinning或Boxing,也未能成功实现。我期望的main代码如下:

let textures = piece::Piece::load_all_textures().await;

let mut game = game::Game::new(player::Player::User, player::Player::User);
let future = game.play();

while None = future.poll() {
    game.render(textures).await;
    next_frame().await;
}

// 游戏结束后的操作

我知道这段代码会存在借用检查问题,但我希望先解决当前的异步循环问题。请问实现该需求的最简方法是什么?

解决方案

最简的实现可以借助Rust异步runtime的工具,避开手动调用poll()的底层操作,同时满足"持续渲染直到游戏结束"的需求。以下是两种实用方案:

方案一:后台任务+循环检查

通过将game.play()放到后台异步任务中,主线程持续渲染并定期检查任务是否完成:

use tokio::task;

#[tokio::main]
async fn main() {
    let textures = piece::Piece::load_all_textures().await;

    let mut game = game::Game::new(player::Player::User, player::Player::User);
    // 将play任务提交到后台
    let game_task = task::spawn(async move {
        game.play().await
    });

    // 循环渲染直到任务完成
    loop {
        if game_task.is_finished() {
            break;
        }
        // 用引用传递textures避免所有权问题
        game.render(&textures).await;
        next_frame().await;
    }

    // 获取游戏结束结果
    let winner = game_task.await.unwrap();
    // 游戏结束后的操作
}

方案二:使用select!宏(更优雅)

select!宏可以同时等待多个Future,哪个先完成就执行对应分支,无需主动轮询:

use tokio::select;

#[tokio::main]
async fn main() {
    let textures = piece::Piece::load_all_textures().await;

    let mut game = game::Game::new(player::Player::User, player::Player::User);
    let play_future = game.play();

    // 自动Pin Future,避免手动处理Pinning逻辑
    tokio::pin!(play_future);

    loop {
        select! {
            // 游戏结束分支
            winner = &mut play_future => {
                break;
            }
            // 渲染分支
            _ = async {
                game.render(&textures).await;
                next_frame().await;
            } => {
                // 继续循环
            }
        }
    }

    // 游戏结束后的操作
}

关于Pinning的简单说明

你之前直接调用poll()失败,是因为很多Rust Future是自引用结构(内部持有指向自身数据的指针),如果Future在内存中移动,这些指针会失效。Pinning的作用就是固定Future在内存中的位置,避免移动导致的错误。tokio::pin!或Box::pin()可以帮你自动处理Pinning,不需要手动操作底层的poll方法。

另外,你提到的借用检查问题,需要调整play和render的签名,比如让两者共享对game的访问(可使用Arc<Mutex<Game>>实现线程安全的共享可变访问),这部分可以后续单独处理。

内容的提问来源于stack exchange,提问作者Ed Jackson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 22:16:31