Skip to content

Give a better error when HRTB require a function to be valid for 'static references #102201

Open
@jyn514

Description

@jyn514

Given the following code: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=cace533e183828ffeb2772f434b0611e

use std::future::Future;
use std::pin::Pin;

type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;

#[derive(Default)]
struct B(bool);

#[derive(Default)]
struct A {
    a: bool,
    b: B,
}

impl A {
    fn b_mut(&mut self) -> &mut B {
        &mut self.b
    }
}

async fn entrypoint() {
    let mut a = A::default();
    let list = vec![];

    foo(&mut a, |b| Box::pin(baz(b, &list))).await;
}

async fn foo<'a, F>(a: &'a mut A, f: F)
where
     F: for<'b> FnOnce(&'b mut B) -> BoxFuture<'b, ()>,
{

    f(a.b_mut()).await;
}

async fn baz(b: &mut B, _: &[u8]) {
    b.0 = !b.0;
}

The current output is:

error[E0597]: `list` does not live long enough
  --> src/lib.rs:25:38
   |
25 |     foo(&mut a, |b| Box::pin(baz(b, &list))).await;
   |                 --- -----------------^^^^--
   |                 |   |                |
   |                 |   |                borrowed value does not live long enough
   |                 |   returning this value requires that `list` is borrowed for `'static`
   |                 value captured here
26 | }
   | - `list` dropped here while still borrowed

This is correct, but hard to understand.

Ideally the output should look like:

error[E0597]: `list` does not live long enough
  --> src/lib.rs:25:38
   |
25 |     foo(&mut a, |b| Box::pin(baz(b, &list))).await;
   |                 --- -----------------^^^^--
   |                 |   |                |
   |                 |   |                borrowed value does not live long enough
   |                 |   returning this value requires that `list` is borrowed for `'static`
   |                 value captured here
26 | }
   | - `list` dropped here while still borrowed
   |
   | = note: the lifetime cannot be shortened from `'static` to `'b` because closures are contravariant: https://doc.rust-lang.org/nomicon/subtyping.html#variance
   |
   | = note: the closure passed to `foo` must be valid for any lifetime, including the `'static` lifetime
28 | async fn foo<'a, F>(a: &'a mut A, f: F)
   |                                   ^^^^
   |      F: for<'b> FnOnce(&'b mut B) -> BoxFuture<'b, ()>,
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
   |         |
   |         due to the bound here

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-async-awaitArea: Async & AwaitA-closuresArea: Closures (`|…| { … }`)A-diagnosticsArea: Messages for errors, warnings, and lintsA-higher-rankedArea: Higher-ranked things (e.g., lifetimes, types, trait bounds aka HRTBs)A-trait-systemArea: Trait systemAsyncAwait-TriagedAsync-await issues that have been triaged during a working group meeting.D-newcomer-roadblockDiagnostics: Confusing error or lint; hard to understand for new users.D-terseDiagnostics: An error or lint that doesn't give enough information about the problem at hand.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