When exactly is `drop` called?

I was under impression that if I call drop then it is the place where desctruction will happen. But I realized it is not the case.
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=4775ba6438a1cd26c0b5f88bfe86790f

mod test1 {
    use std::sync::{Arc, Mutex};
    use std::time::Duration;



    pub async fn spawn(resource: Arc<Mutex<Vec<String>>>) {
        tokio::spawn(async move {
            loop {
                //{
                let guard = resource.lock().unwrap();
                let _list = guard.clone();
                drop(guard);
                //}
                tokio::time::sleep(Duration::from_secs(1)).await;
            }
        });
    }
}
error: future cannot be sent between threads safely
   --> src/bin/tool/main.rs:18:9
    |
18  |         tokio::spawn(async move {
    |         ^^^^^^^^^^^^ future created by async block is not `Send`
    | 
   ::: /home/vadym/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.4.0/src/task/spawn.rs:129:21
    |
129 |         T: Future + Send + 'static,
    |                     ---- required by this bound in `tokio::spawn`
    |
    = help: within `impl Future`, the trait `Send` is not implemented for `std::sync::MutexGuard<'_, Vec<String>>`
note: future is not `Send` as this value is used across an await
   --> src/bin/tool/main.rs:25:17
    |
21  |                 let guard = resource.lock().unwrap();
    |                     ----- has type `std::sync::MutexGuard<'_, Vec<String>>` which is not `Send`
...
25  |                 tokio::time::sleep(Duration::from_secs(1)).await;
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ await occurs here, with `guard` maybe used later
26  |             }
    |             - `guard` is later dropped here

Documentation says that drop is called when value goes out of scope, which I would expect to be drop(guard) line, because guard is not available after this line, thus it is out of scope. Yet compiler indicates that it is actually happen at the end of block.
This problem can be addressed by introducing artificial block (uncoment brackets).

While usually it does not matter, in case of async function it causes an error of non-Send guard object being kept across await.

I'm just surprised that compiler does not take opportunity to drop object early, especially when it could produce more robust code without tricks.

drop(guard) definitely destroys guard. I think this is a case of the compiler not being smart enough to see that and/or an incorrect diagnostic.

This seems to be the main issue tracking this kind of problem with async blocks: async/await: awaiting inside a match block captures borrow too eagerly · Issue #57017 · rust-lang/rust · GitHub

3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.