Deferred tasks in Rust

· todoroki's blog


Annotated version

 1use std::time::Duration;
 2
 3use color_eyre::eyre;
 4
 5struct Deferred<T: FnOnce()> {
 6    task: Option<T>,
 7}
 8
 9impl<T: FnOnce()> Deferred<T> {
10    fn abort(&mut self) {
11        println!("I AM ABORTING");
12        self.task.take();
13    }
14}
15
16impl<T: FnOnce()> Drop for Deferred<T> {
17    fn drop(&mut self) {
18        if let Some(task) = self.task.take() {
19            task();
20        }
21    }
22}
23
24fn defer<T: FnOnce()>(f: T) -> Deferred<T> {
25    Deferred { task: Some(f) }
26}
27
28async fn send_request(url: &str) -> eyre::Result<()> {
29    let mut finish = defer(|| println!("I am done here")); // this pattern is amazing, this is only called when there is no timeout
30    // otherwise abort is called
31    let request = reqwest::get(url);
32    tokio::select! {
33        response = request => {
34            let response = response?;
35            let body = response.text().await?;
36            println!("{:?}", &body[..20]);
37        }
38        _ = tokio::time::sleep(Duration::from_millis(1000)) => {
39            finish.abort();
40            return Err(eyre::eyre!("timeout"))
41        }
42    }
43    Ok(())
44}
45
46#[tokio::main]
47async fn main() {
48    println!("MAKING REQ");
49    send_request("https://www.google.com").await.unwrap();
50}
51