7

I have an async fn that returns a type, and want to implement Drop on that type that calls another async function. It's not clear how to do this, and I can't find anything in the docs. The most illuminating article I found is Asynchronous Destructors by withoutboats, but I don't really understand the reasoning, or where this feature is at.

Shepmaster
  • 326,504
  • 69
  • 892
  • 1,159
Matt Joiner
  • 106,562
  • 103
  • 351
  • 513
  • 3
    The point of the article is precisely to said that it's not currently possible. And propose a possible solution, but AFAIK it doesn't exist yet. – Stargateur Jan 17 '20 at 06:47

2 Answers2

5

It's not clear how to do this, and I can't find anything in the docs

That's because it's not possible; there is no "async Drop". Drop must be synchronous.

See also:

Shepmaster
  • 326,504
  • 69
  • 892
  • 1,159
0

I've implemented an async drop using CancellationTokens for a struct we use in our testing.

Note: This is currently just used in our test code, I wouldn't recommended it for any production code without some serious testing.

struct TestObj {
   drop_token: CancellationToken,
   dropped_token: CancellationToken
}

impl TestObj {
  pub async fn new() -> Self {
    let drop_token = CancellationToken::new();
    let dropped_token = start_drop_watcher(&drop_token).await;

    Self {
      drop_token, 
      dropped_token
    }
  }

  async fn start_drop_watcher(drop_token: &CancellationToken) -> CancellationToken {
    let drop_child = drop_token.child_token();

    let dropped_token = CancellationToken::new();
    let dropped_child = dropped_token.child_token();

    tokio::spawn(async move {
      while !drop_child.is_cancelled() {
        tokio::time::sleep(Duration::from_millis(100)).await;
      }

      // Do async cleanup logic here (probably with a timeout)

      dropped_token.cancel();
    });

    dropped_child
  }
}

impl Drop for TestObj {
  fn drop(&mut self) {
    self.drop_token.cancel();

    while !self.dropped_token.is_cancelled() {
      std::thread::sleep(Duration::from_millis(100));
    }
  }
}

It's also important you run your tests with multi-threading or this won't work.

#[tokio::test(flavor = "multi_thread")]
fn it_does_a_thing() {
  assert!(true);
}
Dan
  • 2,128
  • 18
  • 28