Here, take a look at this snippet of Rust code that’s been stuck in my head for weeks now:
async fn send_request(
url: &str,
metrics: &mut Metrics
) -> Result<String> {
let mut finish = defer(|| metrics.requests += 1);
let request = reqwest::get(url);
tokio::select! {
response = request => {
let response = response?;
let body = response.text().await?;
Ok(body)
}
_ = tokio::time::sleep(Duration::from_millis(2500)) => {
finish.abort();
Err(anyhow::anyhow!("timeout"))
}
}
}
Glance over it — do you understand everything going on here?
A year ago I wouldn’t have — and at that point I had been programming in Rust for three years in my spare time, building a compiler that now comes in at around 15k lines of code.
A not-that-trivial project that made me feel at ease with Rust, but I also knew that the Rust I had been writing wasn’t, uhm, real-world Rust — no 3rd-party dependencies, no concurrency, no panic handling. Nothing that had “for production use ony” on the label.
Then, in the Zed codebase, I got to see what real-world Rust looks like.
And that snippet above — it’s a recreation, a simplified version of some actual code I came across in the Zed code — has stuck with me because, here it is, everything that I didn’t use in Rust for years, compressed into few lines of code. A super-dense representation of a Rust that I hadn’t seen. A window into a world in which Rust works differently. A little prism.
I mean: the defer
call alone — you can probably teach a one-hour Rust class just on that defer
. Take another look at the two lines related to the defer
:
async fn send_request(
url: &str,
metrics: &mut Metrics
) -> Result<String> {
let mut finish = defer(|| metrics.requests += 1);
let request = reqwest::get(url);
tokio::select! {
response = request => {
let response = response?;
let body = response.text().await?;
Ok(body)
}
_ = tokio::time::sleep(Duration::from_millis(2500)) => {
finish.abort();
Err(anyhow::anyhow!("timeout"))
}
}
}
Looks like it bumps metrics.requests
but only if the request doesn’t run into the timeout, because if it does, finish.abort()
is called — but how? First time I came across it I actually said that out loud.
Then I dug in and — to my great fascination — it uses Drop to actually defer the execution of code. Here’s how:
struct Deferred<T: FnOnce()> {
task: Option<T>,
}
impl<T: FnOnce()> Deferred<T> {
fn abort(&mut self) {
self.task.take();
}
}
impl<T: FnOnce()> Drop for Deferred<T> {
fn drop(&mut self) {
if let Some(task) = self.task.take() {
task();
}
}
}
fn defer<T: FnOnce()>(f: T) -> Deferred<T> {
Deferred { task: Some(f) }
}
It creates a Deferred
struct that, when dropped (in our case: at the end of the function), executes the task
it contains — except if abort
had been called before, because then the task
is None
.
I knew of Drop but I had never used it. Certainly haven’t used it to intentionally execute code when someout goes out of scope at the end of a function.
And then there’s the fact that the whole function is async
and that what reqwest::get
returns is actually a Future that needs to be awaited, but you can’t see that because tokio::select!
macro hides it and, wait, you can do something like select(2) in async Rust? What?
And then there’s question marks!
async fn send_request(
url: &str,
metrics: &mut Metrics
) -> Result<String> {
let mut finish = defer(|| metrics.requests += 1);
let request = reqwest::get(url);
tokio::select! {
response = request => {
let response = response?;
let body = response.text().await?;
Ok(body)
}
_ = tokio::time::sleep(Duration::from_millis(2500)) => {
finish.abort();
Err(anyhow::anyhow!("timeout"))
}
}
}
H
ow is that even possible — do the error types match? Doesn’t matter, it works here because guess what, the Result<String>
in the return type — that’s not the Result
that you learned about in the Rust book, it’s anyhow::Result
and you can map anything into that.
Every line looks normal on first glance — your standard Rust 101 code, the code that I had been writing for years — but then, on second and third glance, there’s the realisation that, wait, what, what’s going on here, that has to work differently.
Today I know exactly what the code does and feel comfortable writing it, but still, when I came across that snippet and saw all of the things I didn’t know about for years, compressed into a dozen lines, I couldn’t help but wonder:
3 years! 15k lines of code in a compiler, an uncounted number of side-side projects, worked through multiple books and yet I never came close to code like this!
Fascinating, isn’t it? How a language can be so big that you can comfortably live in one corner of it for years without seeing or hearing about anything else that’s going on on the other side.
That's a bunch of neat ideas crammed into a few lines. The question mark operator is briefly mentioned and the Rust book, but it's a really nice pinch of syntactic sugar. And the defer object is so brilliant I'm genuinely surprised it's not in std.
I will say that overriding "Result" rubs me the wrong way really strongly. Admittently I don't know if this is a more common thing in Rust but it smells kinda funny to me
Great post! I'd enjoy seeing more breakdowns of interesting snippets from Zed. I enjoy your Joy & Curiosity posts a lot as well.