Very confused about async block

Hi. I am relatively new to async programming and have been banging my head against the wall for two hours straight trying to figure out how to solve this, but nothing I have tried so far works. I have this code block that tries to have a simple stream consumer:

async fn get_transaction() -> web3::Result<()> {
    // Create a new websocket connection with a geth or parity node
    let transport = WebSocket::new("ws://localhost:8546").await?;
    // create a struct to act as the gateway to access the Web3 api using that connection
    let web3 = Web3::new(transport);
    // wrap the websocket connection in `EthSubscribe` for pub/sub capabilities
    let eth_sub = web3.eth_subscribe();
    // creates a `Eth`  struct wrapper around the websocket
    let eth = web3.eth();
    // Subscribe the pending transaction stream from the node
    let pending_tx_stream = eth_sub.subscribe_new_pending_transactions().await?;
    pending_tx_stream
        .try_for_each_concurrent(CONCURRENT_CONSUMERS, |tx| async move {
            // Create a transactionid type from the given transaction hash
            let txid = TransactionId::from(tx);
            let tx_details = eth.transaction(txid).await?;
            println!("{:?}", tx_details);
            Ok(())
        })
        .await?;
    Ok(())
}

But it tells me that eth moved inside the generator. If I remove the move from the async block then the tx variable gives me an error telling me that it can outlive the function. Any ideas on how to solve this?

You want tx to be moved into the async block, but you want eth to be shared between the blocks instead of being moved. To do this, you can define

let eth = web3.eth();
let eth_ref = &eth;

then use eth_ref in the async block.

Note that it is not possible to obtain mutable access to eth from the async block when running the async blocks concurrently. (Though it would be possible to make a separate clone of it for each.)

Does the async block try to gain mutable access to all the variables in the context?

Also, thank you. That was really easy solution, but I would have never guessed it.

The rules are:

  1. Without move, it tries to take an immutable borrow of things used immutably, and a mutable borrow of things used mutably.
  2. With move, it tries to take ownership of anything you use inside the block.

To take ownership of some things and a borrow of others, you convince it to take ownership of a reference.

The thing with mutability was more if transaction took &mut self.

1 Like

That makes a lot of sense. Thank you so much!