Skip to content

async fn can't handle multiple lifetimes in a slice of slices #76547

Closed
@asomers

Description

@asomers
Contributor

I tried this code:

use futures::{
    Future,
    task::{Context, Poll}
};
use std::pin::Pin;

pub struct ListFut<'a>(&'a mut [&'a mut [u8]]);

impl<'a> Future for ListFut<'a> {
    type Output = ();

    fn poll(self: Pin<&mut Self>, _cx: &mut Context) -> Poll<Self::Output> {
        unimplemented!()
    }
}

/// The compiler complains that "this parameter and the return type are declared
/// with different lifetimes"
pub async fn readv_at(bufs: &mut [&mut [u8]]) {
    ListFut(bufs).await
}

/// The same thing, but making the lifetimes explicit
pub async fn readv_at2<'a, 'b>(bufs: &'a mut [&'b mut [u8]]) {
    ListFut(bufs).await
}

/// But the compiler accepts this method just fine, because there is only one
/// lifetime.
pub async fn readv_at1<'a>(bufs: &'a mut [&'a mut [u8]]) {
    ListFut(bufs).await
}

/// Even if we explicitly bound one lifetime by the other, the compiler still
/// can't handle it.
pub async fn readv_at3<'a, 'b: 'a>(bufs: &'a mut [&'b mut [u8]]) {
    ListFut(bufs).await
}

I expected to see this happen: No error

Instead, this happened: For every variant with two lifetimes, the compiler complains "these two types are declared with different lifetimes... but data from bufs flows into bufs here"

This looks similar to #56238 , but I see that the commit which closed that issue didn't included any test cases involving slices.

Meta

rustc --version --verbose:

rustc 1.48.0-nightly (d006f5734 2020-08-28)
rustc 1.42.0 (b8cedc004 2020-03-09)

Activity

Matthias247

Matthias247 commented on Sep 10, 2020

@Matthias247
Contributor

As discussed on discord I'm not sure whether this is a bug. It works if ListFut is declared as

pub struct ListFut<'a, 'b>(&'a mut [&'b mut [u8]]);

it works. That allows the 2 lifetimes to be separate - compared the original declaration which forces them to be the same.

It could be that the compiler interprets:

pub async fn readv_at(bufs: &mut [&mut [u8]]) {
    ListFut(bufs).await
}

as

those 2 lifetimes could be different

However I'm not sure whether this makes a difference here. On the first glance it doesn't, because all those references are pure "inputs". Nothing is returned, so as long as the lifetimes are covering the function duration we should be good - independent whether they are the same or different.

However on the other hand readv_at returns a hidden impl Future, which carries the input lifetime forwards. Based on that it could matter if these are different or not.

taiki-e

taiki-e commented on Sep 10, 2020

@taiki-e
Member

This is because elided lifetimes are handled as different lifetimes. (in this case, handled as &'1 mut &'2 mut [u8])

It can be reproduced not only with async functions but also with normal functions:
playground

pub struct ListFut<'a>(&'a mut [&'a mut [u8]]);

// error
pub fn readv_at(bufs: &mut [&mut [u8]]) {
    ListFut(bufs);
}

// error
pub fn readv_at2<'a, 'b>(bufs: &'a mut [&'b mut [u8]]) {
    ListFut(bufs);
}

// ok
pub fn readv_at1<'a>(bufs: &'a mut [&'a mut [u8]]) {
    ListFut(bufs);
}

// error
pub fn readv_at3<'a, 'b: 'a>(bufs: &'a mut [&'b mut [u8]]) {
    ListFut(bufs);
}

// ok
pub fn readv_at4<'a: 'b, 'b>(bufs: &'a mut [&'b mut [u8]]) {
    ListFut(bufs);
}
taiki-e

taiki-e commented on Sep 10, 2020

@taiki-e
Member
/// Even if we explicitly bound one lifetime by the other, the compiler still
/// can't handle it.
pub async fn readv_at3<'a, 'b: 'a>(bufs: &'a mut [&'b mut [u8]]) {
    ListFut(bufs).await
}

fwiw, 'b overlives than 'a, so this fails to compile, but you could compile it using a lifetime bound like 'a: 'b, 'b.

added
A-diagnosticsArea: Messages for errors, warnings, and lints
D-confusingDiagnostics: Confusing error or lint that should be reworked.
on Sep 15, 2020
tmandry

tmandry commented on Sep 15, 2020

@tmandry
Member

Discussed in our async foundations triage meeting today.

We can solve this in two parts.. one would be to change the wording "...but data from bufs is returned here" in the async case to make it more clear that we're returning a future (or just not use the special case wording for returned at all.)
EDIT: Added E- labels for this first part.

The other is not async-related, really, but has to do with improving the error message wording and suggestion.

added
E-easyCall for participation: Easy difficulty. Experience needed to fix: Not much. Good first issue.
E-help-wantedCall for participation: Help is requested to fix this issue.
E-mentorCall for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion.
on Sep 15, 2020
added
AsyncAwait-TriagedAsync-await issues that have been triaged during a working group meeting.
on Sep 22, 2020
added a commit that references this issue on Nov 5, 2020

Rollup merge of rust-lang#76765 - guswynn:async_return, r=tmandry

dea29bd

18 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-async-awaitArea: Async & AwaitA-diagnosticsArea: Messages for errors, warnings, and lintsA-lifetimesArea: Lifetimes / regionsAsyncAwait-PolishAsync-await issues that are part of the "polish" areaAsyncAwait-TriagedAsync-await issues that have been triaged during a working group meeting.C-bugCategory: This is a bug.D-confusingDiagnostics: Confusing error or lint that should be reworked.E-easyCall for participation: Easy difficulty. Experience needed to fix: Not much. Good first issue.E-help-wantedCall for participation: Help is requested to fix this issue.E-mentorCall for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion.

    Type

    No type

    Projects

    Status

    Done

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @eholk@asomers@nikomatsakis@jonas-schievink@tmandry

        Issue actions

          async fn can't handle multiple lifetimes in a slice of slices · Issue #76547 · rust-lang/rust