Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

AFIT does not fulfill the required lifetime, manual RPITIT does #133676

Closed
fasterthanlime opened this issue Nov 30, 2024 · 6 comments
Closed

AFIT does not fulfill the required lifetime, manual RPITIT does #133676

fasterthanlime opened this issue Nov 30, 2024 · 6 comments
Labels
C-discussion Category: Discussion or questions that doesn't represent real issues. T-types Relevant to the types team, which will review and decide on the PR/issue.

Comments

@fasterthanlime
Copy link
Contributor

fasterthanlime commented Nov 30, 2024

(For the casual reader: "AFIT" = Async Fn In Trait, "RPITIT" = Return Position Impl Trait In Trait")

I tried this code: (playground)

use std::{borrow::Cow, future::Future};

trait Deserializer<'s> {
    fn pop(&mut self) -> impl Future<Output = Cow<'s, str>> + '_;
}

struct Works;
struct DoesNotWork;

struct Journal {
    items: Vec<Cow<'static, str>>,
}

impl<'s> Deserializer<'s> for (Journal, Works) {
    // works
    fn pop(&mut self) -> impl Future<Output = Cow<'s, str>> + '_ {
        async move { self.0.items.pop().unwrap_or_default() }
    }
}

impl<'s> Deserializer<'s> for (Journal, DoesNotWork) {
    // doesn't work
    async fn pop(&mut self) -> Cow<'s, str> {
        self.0.items.pop().unwrap_or_default()
    }
}

fn main() {
    // good luck michael! love u
}

I expected to see this happen: everything typechecks.

Instead, this happened:

error[E0477]: the type `impl Future<Output = Cow<'s, str>>` does not fulfill the required lifetime
  --> src/main.rs:23:5
   |
23 |     async fn pop(&mut self) -> Cow<'s, str> {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
note: type must outlive the anonymous lifetime defined here as required by this binding
  --> src/main.rs:23:18
   |
23 |     async fn pop(&mut self) -> Cow<'s, str> {
   |                  ^^^^^^^^^

Minimal repro (edit: that's a different issue, see below)

We don't need to bring AFIT into it: (playground)

use std::future::Future;

fn assert_future_unit_covariant_workaround<'s>(
    f: impl Future<Output = &'static ()> + 'static,
) -> impl Future<Output = &'s ()> + 's {
    // this works
    async move { f.await }
}

fn assert_future_unit_covariant<'s>(
    f: impl Future<Output = &'static ()> + 'static,
) -> impl Future<Output = &'s ()> + 's {
    // rustc: lifetime may not live long enough
    // returning this value requries that 's must outlive 'static
    f
}

Meta

rustc --version --verbose:

rustc 1.83.0 (90b35a623 2024-11-26)
binary: rustc
commit-hash: 90b35a6239c3d8bdabc530a6a0816f7ff89a0aaf
commit-date: 2024-11-26
host: aarch64-apple-darwin
release: 1.83.0
LLVM version: 19.1.1
@fasterthanlime fasterthanlime added the C-bug Category: This is a bug. label Nov 30, 2024
@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Nov 30, 2024
@compiler-errors
Copy link
Member

For the record, the minimization you provided is a totally different issue than the original issue.

@compiler-errors
Copy link
Member

I don't expect the "minimized example" to pass (trait impls are not "covariant" over their output associated types), but I'm a bit surprised the original example doesn't pass.

@compiler-errors
Copy link
Member

Actually, this is a valid error.

The RPIT in the trait definition (impl Future<Output = &'s ()> + '_) is stronger than the RPIT in the impl (impl Future<Output = &'s ()>) since the one in the trait definition is essentially saying "I only capture data from the '_ lifetime in &self", whereas the one in the impl makes no such guarantee.

There's no implied outlives bound between 's and '_ given by the signature of the trait, so the compiler really just can't prove what it needs to.

@fasterthanlime
Copy link
Contributor Author

There's no implied outlives bound between 's and '_ given by the signature of the trait, so the compiler really just can't prove what it needs to.

Oh, mhh so the fix I'm looking for is just this?

use std::{borrow::Cow, future::Future};

trait Deserializer<'s> {
    fn pop<'this>(&'this mut self) -> impl Future<Output = Cow<'s, str>> + 'this
    where
        's: 'this;
}

struct Works;
struct AlsoWorks;

struct Journal {
    items: Vec<Cow<'static, str>>,
}

impl<'s> Deserializer<'s> for (Journal, Works) {
    // works
    fn pop<'this>(&'this mut self) -> impl Future<Output = Cow<'s, str>> + 'this
    where
        's: 'this,
    {
        async move { self.0.items.pop().unwrap_or_default() }
    }
}

impl<'s> Deserializer<'s> for (Journal, AlsoWorks) {
    // also work
    async fn pop<'this>(&'this mut self) -> Cow<'s, str>
    where
        's: 'this,
    {
        self.0.items.pop().unwrap_or_default()
    }
}

fn main() {
    // mhhh
}

@compiler-errors
Copy link
Member

If that bound makes sense in the design of your API, then sure.

fasterthanlime added a commit to bearcove/merde that referenced this issue Nov 30, 2024
@fasterthanlime
Copy link
Contributor Author

Fantastic! Thanks for helping me get out of this one.

@saethlin saethlin added C-discussion Category: Discussion or questions that doesn't represent real issues. T-types Relevant to the types team, which will review and decide on the PR/issue. and removed C-bug Category: This is a bug. needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. labels Dec 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-discussion Category: Discussion or questions that doesn't represent real issues. T-types Relevant to the types team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

4 participants