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

Async fn implicit capture requirement is confusing #103470

Closed
jkelleyrtp opened this issue Oct 24, 2022 · 3 comments
Closed

Async fn implicit capture requirement is confusing #103470

jkelleyrtp opened this issue Oct 24, 2022 · 3 comments

Comments

@jkelleyrtp
Copy link
Contributor

jkelleyrtp commented Oct 24, 2022

I've never really seen this documented anywhere, but as a result of

#60388 which solved #60203,

there is now a weird discrepancy between regular fns and async fns when that makes it confusing to "upgrade" an fn to an async fn.

In particular, with implicit lifetimes, many users would not need to know about the '_ lifetime being necessary.

In the simplest case, take this struct

struct Context<'a> { inner: &'a Inner }

with a regular fn, we can pass it into the arguments with complete elision

fn run(cx: Context) {}

However, if we want to upgrade the the function to be async:

async fn run(cx: Context) {}

we are greeted with an error:

error[E0726]: implicit elided lifetime not allowed here
  --> example.rs:71:20
   |
71 | async fn run(cx: Context) {}
   |                    ^^^ expected lifetime parameter
   |
   = note: assuming a `'static` lifetime...
help: indicate the anonymous lifetime
   |
71 | async fn run(cx: Context <'_>) {}
   |                     ++++

This is not an obvious error, especially given that for all other functions, NLL kicks in and this is never a requirement.

I'd personally prefer to not require the elided lifetime on async fns when the regular fn does not need it either.

In 2019, this decision was made as a way of "future proofing" async and to fix a specific bug. However, I think for the sake of consistency, we should loosen the restrictions and allow NLL to work the same in async fns as it does in regular fns.

@KamilaBorowska
Copy link
Contributor

KamilaBorowska commented Oct 24, 2022

Hidden lifetime parameters in types are deprecated, if you use #![warn(rust_2018_idioms)] you will see a warning:

warning: hidden lifetime parameters in types are deprecated
 --> src/main.rs:7:12
  |
7 | fn run(cx: Context) {}
  |            ^^^^^^^ expected lifetime parameter
  |
note: the lint level is defined here
 --> src/main.rs:1:9
  |
1 | #![warn(rust_2018_idioms)]
  |         ^^^^^^^^^^^^^^^^
  = note: `#[warn(elided_lifetimes_in_paths)]` implied by `#[warn(rust_2018_idioms)]`
help: indicate the anonymous lifetime
  |
7 | fn run(cx: Context<'_>) {}
  |                   ++++

async fn never allowed elided lifetimes, so Rust compiler disallows them here without a warning.

@jkelleyrtp
Copy link
Contributor Author

In the explanation for the lint, it's noted that this lint is off by default since "it has some known issues" and "may require significant transition for old code."

I would argue that one of the issues is that an implicit lifetime is required to be annotated in async fns but not in sync fns when there's no need for annotation at all since there is no borrowing.

@KamilaBorowska
Copy link
Contributor

KamilaBorowska commented Oct 24, 2022

There are plans to enable this lint by default, whatever for all lifetime parameters, or just lifetimes that participate in elision, see #91639. Whatever way that would go, in async functions, all lifetimes participate in elision and as such be warned against, for instance the following:

async fn run(cx: Context<'_>) {
    do_something_with(cx).await;
}

Is a syntactic sugar for this:

fn run<'a>(cx: Context<'a>) -> impl std::future::Future<Output = ()> + 'a {
    async move {
        do_something_with(cx).await;
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants