Futures in Rust
In this lesson, you will learn about Futures in Rust.
Futures are the core building blocks behind async and await.
Understanding futures helps you deeply understand how async Rust works internally.
What Is a Future?
A Future represents a value that will be available at some point in the future.
Instead of producing a result immediately, a future describes a computation that may not have completed yet.
Key Idea of Futures
A future is:
- Lazy (does nothing until polled)
- Non-blocking
- Driven by an async runtime
In Rust, async functions return futures automatically.
Futures and async Functions
When you define an async function, Rust transforms it into a function that returns a future.
async fn add(a: i32, b: i32) -> i32 {
a + b
}
Calling this function does not compute the result immediately. It returns a future instead.
Awaiting a Future
The .await keyword is used to wait for a future to complete.
let result = add(2, 3).await;
println!("{}", result);
The runtime pauses execution until the future is ready, without blocking the thread.
How Futures Work Internally
Internally, a future is polled by the async runtime. Each poll asks:
- Is the result ready?
- If not, what should be done next?
If the future is not ready, it returns Poll::Pending.
If it is ready, it returns Poll::Ready.
The Future Trait
Futures in Rust implement the Future trait.
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
trait Future {
type Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context)
-> Poll;
}
Most of the time, you do not need to implement this trait yourself.
Manual Future Example
Below is a simplified example of a custom future.
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
struct ReadyFuture;
impl Future for ReadyFuture {
type Output = i32;
fn poll(self: Pin<&mut Self>, _: &mut Context)
-> Poll {
Poll::Ready(10)
}
}
This future immediately returns a value when polled.
Futures Are Lazy
A future does nothing until it is awaited or polled by the runtime.
Simply creating a future does not start execution.
let future = add(1, 2); // not executed yet
Chaining Futures
Futures can be chained together using async/await.
async fn process() {
let a = add(2, 3).await;
let b = add(a, 4).await;
println!("{}", b);
}
Each future completes before the next one starts.
Why Futures Matter
Futures enable:
- High-performance async systems
- Scalable web servers
- Non-blocking I/O
- Efficient concurrency
They are fundamental to modern Rust applications.
📝 Practice Exercises
Exercise 1
Explain what a future represents.
Exercise 2
Write an async function and identify its future.
Exercise 3
Explain the difference between Pending and Ready.
Exercise 4
Why are futures called lazy?
✅ Practice Answers
Answer 1
A future represents a value that will be available later.
Answer 2
async fn work() -> i32 {
5
}
Answer 3
Pending means the value is not ready yet.
Ready means the computation has completed.
Answer 4
Futures are lazy because they do nothing until polled or awaited.
What’s Next?
In the next lesson, you will learn about Tokio Runtime, which is responsible for executing futures efficiently.