rust-lang / rust-lang/rust

`async fn` can assign to argument to change its lifetime, but normal `fn` and `async` blocks cannot.

Open
#148,389 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-async-await A-borrow-checker A-lifetimes C-bug needs-triage T-types
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

I tried this code:

#![allow(unused)]
async fn works<'a>(mut a: &'a i32, b: i32) {
    a = &b;
}
fn fails<'a>(mut a: &'a i32, b: i32) {
    a = &b;
}
fn also_fails<'a>(mut a: &'a i32, b: i32) -> impl Future<Output = ()> {
    async move {
        a = &b;
    }
}

I expected all 3 functions to compile, or all to fail to compile, but instead only the first function compiles:

error[E0521]: borrowed data escapes outside of async block
  --> src/lib.rs:10:9
   |
 8 | fn also_fails<'a>(mut a: &'a i32, b: i32) -> impl Future<Output = ()> {
   |                   ----- `a` declared here, outside of the async block body
 9 |     async move {
10 |         a = &b;
   |         ^^^^--
   |         |   |
   |         |   borrow is only valid in the async block body
   |         reference to `b` escapes the async block body here

error[E0597]: `b` does not live long enough
 --> src/lib.rs:6:9
  |
5 | fn fails<'a>(mut a: &'a i32, b: i32) {
  |          --                  - binding `b` declared here
  |          |
  |          lifetime `'a` defined here
6 |     a = &b;
  |     ----^^
  |     |   |
  |     |   borrowed value does not live long enough
  |     assignment requires that `b` is borrowed for `'a`
7 | }
  |  - `b` dropped here while still borrowed

Some errors have detailed explanations: E0521, E0597.
For more information about an error, try `rustc --explain E0521`.

Edit: The part below is filed separately as https://github.com/rust-lang/rust/issues/148401

A different issue that's now filed separately

Additionally, there's this variant which has a very strange diagnostic that I don't understand:

#![allow(unused)]
fn weird<'a, 'b>(mut a: &'a i32, b: &'b i32) -> impl Future<Output = ()> + use<'a, 'b> {
    async move {
        a = b;
    }
}
error[E0700]: hidden type for `impl Future<Output = ()>` captures lifetime that does not appear in bounds
 --> src/lib.rs:3:5
  |
2 |   fn weird<'a, 'b>(mut a: &'a i32, b: &'b i32) -> impl Future<Output = ()> + use<'a, 'b> {
  |                                                   -------------------------------------- opaque type defined here
3 | /     async move {
4 | |         a = b;
5 | |     }
  | |_____^
  |
  = note: hidden type `{async block@src/lib.rs:3:5: 3:15}` captures lifetime `'_`

For more information about this error, try `rustc --explain E0700`.

See also #128225, where a variable can be assigned a value with a different lifetime.

Related to #148463, where a similar inconsistency occurs with patterns in function arguments.

Meta

Reproducible on the playground with version 1.93.0-nightly (2025-11-01 bd3ac0330018c23b111b)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the three snippets on the reported Rust 1.93.0-nightly playground and compare their lifetime behavior and diagnostics. Read the related issues #128225 and #148463 for context; done means the inconsistency is resolved or clearly documented with matching compiler behavior and diagnostics.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.