Sivaram Konanki

February 10, 2025

Deferred tasks in Rust

Annotated version

use std::time::Duration;

use color_eyre::eyre;

struct Deferred<T: FnOnce()> {
    task: Option<T>,
}

impl<T: FnOnce()> Deferred<T> {
    fn abort(&mut self) {
        println!("I AM ABORTING");
        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) }
}

async fn send_request(url: &str) -> eyre::Result<()> {
    let mut finish = defer(|| println!("I am done here")); // this pattern is amazing, this is only called when there is no timeout
    // otherwise abort is called
    let request = reqwest::get(url);
    tokio::select! {
        response = request => {
            let response = response?;
            let body = response.text().await?;
            println!("{:?}", &body[..20]);
        }
        _ = tokio::time::sleep(Duration::from_millis(1000)) => {
            finish.abort();
            return Err(eyre::eyre!("timeout"))
        }
    }
    Ok(())
}

#[tokio::main]
async fn main() {
    println!("MAKING REQ");
    send_request("https://www.google.com").await.unwrap();
}