Skip to content

Consider async blocks on lifetime errors #64382

Closed
@estebank

Description

@estebank

The following incorrect code

        // For QoS, this will only accept one message and output that
        // receive all inbound messages

        // TODO: this should match edns settings
        loop {
            let msg = if let Some(receiving) = receiving {
                // TODO: should we drop this packet if it's not from the same src as dest?
                let msg = ready!(receiving.as_mut().poll(cx))?;

                Some(Poll::Ready(Some(Ok(msg))))
            } else {
                None
            };
 
            *receiving = None;

            if let Some(msg) = msg {
                return msg;
            }

            let socket = Arc::clone(socket);
            let mut buf = [0u8; 2048];
            let receive_future = async {
                let socket = socket;

                let mut socket = socket.lock().await;
                let (len, src) = socket.recv_from(&mut buf).await?;
                
                Ok(SerialMessage::new(
                    buf.iter().take(len).cloned().collect(),
                    src,
                ))
            };

            *receiving = Some(Box::pin(receive_future));
        }

emits the following error

error[E0597]: `buf` does not live long enough
   --> crates/proto/src/udp/udp_stream.rs:204:56
    |
183 |               let msg = if let Some(receiving) = receiving {
    |                                                  --------- borrow later used here
...
200 |               let receive_future = async {
    |  ________________________________________-
201 | |                 let socket = socket;
202 | |
203 | |                 let mut socket = socket.lock().await;
204 | |                 let (len, src) = socket.recv_from(&mut buf).await?;
    | |                                                        ^^^ borrowed value does not live long enough
...   |
209 | |                 ))
210 | |             };
    | |_____________- value captured here by generator
...
213 |           }
    |           - `buf` dropped here while still borrowed

The correct code needs the buf to be part of the async block because it is a stack allocation that needs to be in the generator:

        loop {
            let msg = if let Some(receiving) = receiving {
                // TODO: should we drop this packet if it's not from the same src as dest?
                let msg = ready!(receiving.as_mut().poll(cx))?;

                Some(Poll::Ready(Some(Ok(msg))))
            } else {
                None
            };
 
            *receiving = None;

            if let Some(msg) = msg {
                return msg;
            }

            let socket = Arc::clone(socket);
            let receive_future = async {
                let socket = socket;
                let mut buf = [0u8; 2048];

                let mut socket = socket.lock().await;
                let (len, src) = socket.recv_from(&mut buf).await?;
                
                Ok(SerialMessage::new(
                    buf.iter().take(len).cloned().collect(),
                    src,
                ))
            };

            *receiving = Some(Box::pin(receive_future));
        }

Ideally, the error message would mention buf:

error[E0597]: `buf` does not live long enough
   --> crates/proto/src/udp/udp_stream.rs:204:56
    |
183 |               let msg = if let Some(receiving) = receiving {
    |                                                  --------- borrow later used here
...
199 |               let mut buf = [0u8; 2048];
    |                       --- consider moving this stack allocation inside the `async` block
200 |               let receive_future = async {
    |  ________________________________________-
201 | |                 let socket = socket;
202 | |
203 | |                 let mut socket = socket.lock().await;
204 | |                 let (len, src) = socket.recv_from(&mut buf).await?;
    | |                                                        ^^^ borrowed value does not live long enough
...   |
209 | |                 ))
210 | |             };
    | |_____________- value captured here by `async` block
...
213 |           }
    |           - `buf` dropped here while still borrowed

thanks to @bluejekyll for bringing this up!

CC @nikomatsakis

Metadata

Metadata

Assignees

Labels

A-async-awaitArea: Async & AwaitA-borrow-checkerArea: The borrow checkerA-diagnosticsArea: Messages for errors, warnings, and lintsAsyncAwait-PolishAsync-await issues that are part of the "polish" areaAsyncAwait-TriagedAsync-await issues that have been triaged during a working group meeting.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions