Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Async Programming: CompletableFuture vs Rust Future
异步编程:CompletableFuture 与 Rust Future

What you’ll learn: The runtime model behind Rust async, how it differs from Java’s eager futures, and which patterns correspond to CompletableFuture, executors, and timeouts.
本章将学习: Rust async 背后的运行时模型、它和 Java eager future 的区别,以及哪些模式分别对应 CompletableFuture、执行器和超时控制。

Difficulty: 🔴 Advanced
难度: 🔴 高级

Rust and Java both talk about futures, but the execution model is not the same.
Rust 和 Java 都会讲 future,但两边的执行模型并不是一回事。

The First Big Difference: Rust Futures Are Lazy
第一个大差异:Rust future 是惰性的

CompletableFuture<String> future =
    CompletableFuture.supplyAsync(() -> fetchFromRemote());

That Java future starts work as soon as it is scheduled on an executor.
这类 Java future 一旦被执行器接管,任务就开始推进了。

#![allow(unused)]
fn main() {
async fn fetch_from_remote() -> String {
    "done".to_string()
}

let future = fetch_from_remote();
// nothing happens yet
let value = future.await;
}

In Rust, creating the future does not start execution. Polling by an executor starts progress.
在 Rust 里,光把 future 创建出来并不会自动执行。只有被执行器轮询之后,任务才会真正推进。

Why Tokio Exists
为什么 Rust 里会有 Tokio

Java ships with threads, executors, and a rich runtime by default. Rust does not include a default async runtime in the language. That is why libraries such as Tokio exist.
Java 默认就带着线程、执行器和比较完整的运行时能力。Rust 语言本身没有默认 async 运行时,所以才会有 Tokio 这种库承担调度器、定时器和 IO 驱动的职责。

#[tokio::main]
async fn main() {
    let body = reqwest::get("https://example.com")
        .await
        .unwrap()
        .text()
        .await
        .unwrap();

    println!("{body}");
}

The runtime owns the scheduler, timers, IO drivers, and task system.
运行时负责调度器、定时器、IO 驱动和任务系统。

Mental Mapping
心智映射表

JavaRust
CompletableFuture<T>Future<Output = T>
ExecutorServiceTokio runtime or another async executor
Tokio 运行时或其他异步执行器
CompletableFuture.allOf(...)join! or try_join!
orTimeout(...)tokio::time::timeout(...)
cancellation
取消
dropping the future or explicit cancellation primitives
丢弃 future 或使用显式取消原语

Concurrency Pattern: Wait for Many Tasks
并发模式:等待多个任务

var userFuture = client.fetchUser(id);
var ordersFuture = client.fetchOrders(id);

var result = userFuture.thenCombine(ordersFuture, Combined::new);
#![allow(unused)]
fn main() {
let user = fetch_user(id);
let orders = fetch_orders(id);

let (user, orders) = tokio::join!(user, orders);
}

Rust keeps the control flow flatter. The combined result is often easier to read because .await and join! look like normal program structure instead of chained callbacks.
Rust 通常会让控制流更平。.await 和 join! 看起来更像普通程序结构,而不是一串层层套下去的回调拼接。

Timeouts and Cancellation
超时与取消

#![allow(unused)]
fn main() {
use std::time::Duration;

let result = tokio::time::timeout(Duration::from_secs(2), fetch_user(42)).await;
}

When a future is dropped, its work is cancelled unless it was explicitly spawned elsewhere. That is a major conceptual difference from Java code that assumes executor-managed tasks continue until completion.
如果一个 future 被丢弃,它的工作通常也就跟着取消,除非这份工作已经被显式 spawn 到别处。这和很多 Java 代码默认“交给执行器之后它会自己跑完”的思路差别很大。

Spawning Background Work
派生后台任务

#![allow(unused)]
fn main() {
let handle = tokio::spawn(async move {
    expensive_job().await
});

let value = handle.await.unwrap();
}

This is the closest match to scheduling work on an executor and retrieving the result later.
这和把任务提交给执行器、稍后再取结果的思路最接近。

Practical Advice for Java Developers
给 Java 开发者的实际建议

  • Learn the difference between “constructing a future” and “driving a future”.
    先把“创建 future”和“驱动 future 执行”这两个动作彻底分开。
  • Reach for join!, select!, and timeout early; they cover most day-one patterns.
    尽早熟悉 join!、select!、timeout,入门阶段的大多数模式都离不开它们。
  • Be careful with blocking APIs inside async code. Use dedicated blocking pools when needed.
    在 async 代码里要小心阻塞 API,确实要阻塞时就切到专门的阻塞线程池。
  • Treat async Rust as a separate runtime model, not as Java async with different syntax.
    把 async Rust 当成一套独立运行时模型来看,不要把它当成“只是语法不同的 Java async”。

Once this clicks, Rust async stops feeling mysterious and starts feeling mechanically predictable.
这层一旦想通,Rust async 就不会再显得玄乎,反而会变得非常机械、非常可预测。