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

