编程系统程序员

Rust中的异步性和Future的工作原理是什么?async/await的实现有什么独特之处,与其他语言的类似机制有何不同?

用 Hintsage AI 助手通过面试

答案

Rust中的异步性通过Future实现——这是一个代表未来将获得的工作的对象。使用async fn声明的函数返回一个实现Future特征的匿名生成器结构体。要运行异步代码,需要使用运行时(例如,Tokio或async-std),因为标准库不包含内置的事件循环。

Rust的主要特点:

  • 零成本抽象——编译器将async代码转换为状态机,如果任务允许,则不会在堆上分配。
  • 没有本地垃圾收集器——所有资源显式管理,这要求特别关注生命周期和所有权。
  • Send/Sync——在线程之间的任务迁移和同步的限制。

示例:

use tokio::time::sleep; use std::time::Duration; async fn foo() { println!("Hello"); sleep(Duration::from_secs(1)).await; println!("World!"); } #[tokio::main] async fn main() { foo().await; }

陷阱问题

Rust中的异步函数是否可以在调用时默认立即执行?为什么?

答案: 不可以。调用async fn返回的不是结果,而是Future类型的对象(状态机)。要进行实际执行,需要调用.await或将Future传递给运行时。调用本身仅创建任务的描述,但不执行它。

示例:

async fn answer() -> u32 { 42 } let fut = answer(); // 这里没有执行,仅创建future let result = fut.await; // 执行在这里开始

关于对主题细节不够了解造成的实际错误示例


故事

在高负载后端项目中,一名初级开发者声明了几个async函数,但没有在任何地方调用.await。因此主要线程以同步方式执行,导致性能下降和响应时间增长了3倍。

故事

在微服务中使用了异步API,通过tokio进行交互。在迁移时,开发者尝试同时使用async-std和tokio,忘记了事件循环只能有一个。导致了挂起和运行时恐慌,因为两个运行时发生了冲突。

故事

团队中的一名成员忘记了对于Future内部使用的类型的Send/Sync限制。在试图在线程之间共享future时,应用程序因编译错误崩溃,要求某结构必须实现Send,这需要重新审视状态存储架构。