Getting deadlock inside match of async function

I'm getting a deadlock on the following example:

use tokio::net::TcpListener;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use futures::lock::Mutex;
use std::sync::Arc;

struct A{
    
}

impl A {
    pub async fn do_something(&self) -> std::result::Result<(), ()>{
        Err(())
    }
}

async fn lock_and_use(a: Arc<Mutex<A>>) {
    match a.clone().lock().await.do_something().await {
        Ok(()) => {
            
        },
        Err(()) => {
            //try again on error
            println!("trying again");
            a.clone().lock().await.do_something().await.unwrap();
        }
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!("begin");
    let a = Arc::new(Mutex::new(A{}));
    lock_and_use(a.clone()).await;
    println!("end");
    Ok(())
}

Playground

If I did this:

a.clone().lock().await.do_something().await;
a.clone().lock().await.do_something().await;

there would be no problem, the lock() dies on the same line it's created. I thought this principle would be the same for match. If you think about match, it locks the value, calls do_something, awaits on it, and then compares the value. It's true that do_something returns a future which capture the self, but when we await on it, it should discard the self. Why it still holds self? How can I solve this without cloning the result?

You need to do this:

let value = a.clone().lock().await.do_something().await;
match value {
    ...
}

Consider also reading these articles:

  1. Shared state - Tokio tutorial
  2. Async: What is blocking?

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.