Working with Futures
In our last article, we were introduced to Async/Await in Rust. Now, let's take a closer look at the Future
trait, which is the foundation of async
/.await
.
📚 Prerequisites
- A good understanding of
async
/.await
in Rust. - Familiarity with Rust's trait system.
🎯 Article Outline: What You'll Master
In this article, you will learn:
- ✅ The
Future
Trait: What it is and how it works. - ✅ Polling a Future: How an async runtime polls a future to drive it to completion.
- ✅ Building Your Own Future: How to manually implement the
Future
trait.
🧠 Section 1: The Core Concepts of the Future
Trait
The Future
trait has a single method, poll
:
trait Future {
type Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
}
Key Principles:
Output
: The type of value that the future will produce when it's ready.poll
: Thepoll
method is called by the async runtime to check if the future is ready.Poll<T>
: Thepoll
method returns aPoll<T>
, which can be eitherPoll::Ready(T)
orPoll::Pending
.
💻 Section 2: Deep Dive - How Futures are Polled
When you .await
a future, the async runtime calls its poll
method.
- If
poll
returnsPoll::Ready(value)
, then.await
returns thevalue
. - If
poll
returnsPoll::Pending
, the runtime will park the current task and wake it up later when it might be able to make progress.
🛠️ Section 3: Project-Based Example: Implementing a Simple Future
Let's implement a simple future that completes after a certain amount of time:
// main.rs
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::{Duration, Instant};
struct Delay {
when: Instant,
}
impl Future for Delay {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
if Instant::now() >= self.when {
Poll::Ready(())
} else {
// Ignore waker for simplicity
Poll::Pending
}
}
}
#[tokio::main]
async fn main() {
let when = Instant::now() + Duration::from_millis(10);
let delay = Delay { when };
delay.await;
println!("done!");
}
The Goal: To create a custom future that completes after a delay.
The Plan:
- Define a struct to hold the state of our future.
- Implement the
Future
trait for the struct. - Use our custom future in an
async
function.
✨ Section 6: Best Practices and Anti-Patterns in Rust
Rust Best Practices (Idiomatic Way):
- Do this: Use
async
/.await
instead of manually implementingFuture
whenever possible.
💡 Conclusion & Key Takeaways
You've learned about the Future
trait and how it's the foundation of async
/.await
in Rust.
Let's summarize the key Rust takeaways:
- The
Future
trait represents a value that may not be ready yet. - Async runtimes poll futures to drive them to completion.
- You can implement the
Future
trait manually, but it's often not necessary.
➡️ Next Steps
In the next article, "Project: A Multi-threaded Web Server (Part 1)", we'll put everything we've learned about concurrency together to build a real-world application.