Skip to content

Consider async blocks on lifetime errors #64382

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Closed
estebank opened this issue Sep 11, 2019 · 2 comments · Fixed by #65166
Closed

Consider async blocks on lifetime errors #64382

estebank opened this issue Sep 11, 2019 · 2 comments · Fixed by #65166
Assignees
Labels
A-async-await Area: Async & Await A-borrow-checker Area: The borrow checker A-diagnostics Area: Messages for errors, warnings, and lints AsyncAwait-Polish Async-await issues that are part of the "polish" area AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@estebank
Copy link
Contributor

estebank commented Sep 11, 2019

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

@estebank estebank added A-diagnostics Area: Messages for errors, warnings, and lints A-borrow-checker Area: The borrow checker T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. A-async-await Area: Async & Await AsyncAwait-Unclear labels Sep 11, 2019
@cramertj
Copy link
Member

We could also consider suggesting an async move block, similar to the hints we provide for closures.

@cramertj cramertj added AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. and removed AsyncAwait-Unclear labels Sep 24, 2019
@nikomatsakis nikomatsakis added AsyncAwait-OnDeck AsyncAwait-Polish Async-await issues that are part of the "polish" area and removed AsyncAwait-OnDeck AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. labels Oct 1, 2019
@nikomatsakis
Copy link
Contributor

Marking this as a focus item.

@rustbot assign @csmoe

@nikomatsakis nikomatsakis added the AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. label Oct 8, 2019
Centril added a commit to Centril/rust that referenced this issue Oct 9, 2019
Suggest to add `move` keyword for generator capture

 Closes rust-lang#64382
r? @estebank
@bors bors closed this as completed in 5bbdcb6 Oct 9, 2019
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
A-async-await Area: Async & Await A-borrow-checker Area: The borrow checker A-diagnostics Area: Messages for errors, warnings, and lints AsyncAwait-Polish Async-await issues that are part of the "polish" area AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants