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 通常会让控制流更平。.awaitjoin! 看起来更像普通程序结构,而不是一串层层套下去的回调拼接。

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 就不会再显得玄乎,反而会变得非常机械、非常可预测。