Skip to content

Cannot infer lifetime with disjoint capture. #88431

Closed
@ehuss

Description

@ehuss

I tried this code:

#![warn(rust_2021_incompatible_closure_captures)]
#![feature(capture_disjoint_fields)]
use std::collections::HashMap;
use std::future::Future;
use std::pin::Pin;

pub struct GameMode {}

struct GameStateManager<'a> {
    gamestate_stack: Vec<Box<dyn GameState<'a> + 'a>>,
}

pub trait GameState<'a> {}

async fn construct_gamestate_replay<'a>(
    _gamemode: &GameMode,
    _factory: &mut GameStateManager<'a>,
) -> Box<dyn GameState<'a> + 'a> {
    unimplemented!()
}

type FutureGameState<'a, 'b> = Pin<Box<dyn Future<Output = Box<dyn GameState<'a> + 'a>> + 'b>>;

struct MenuOption<'a> {
    command: Box<dyn for<'b> Fn(&'b mut GameStateManager<'a>) -> FutureGameState<'a, 'b> + 'a>,
}

impl<'a> MenuOption<'a> {
    fn new(
        _command: impl for<'b> Fn(&'b mut GameStateManager<'a>) -> FutureGameState<'a, 'b> + 'a,
    ) -> Self {
        unimplemented!()
    }
}

struct MenuState<'a> {
    options: Vec<MenuOption<'a>>,
}

impl<'a> GameState<'a> for MenuState<'a> {}

pub async fn get_replay_menu<'a>(
    gamemodes: &'a HashMap<&str, GameMode>,
) -> Box<dyn GameState<'a> + 'a> {
    let recordings: Vec<String> = vec![];
    let _ = recordings
        .into_iter()
        .map(|entry| {
            MenuOption::new(move |f| {
                Box::pin(construct_gamestate_replay(&gamemodes[entry.as_str()], f))
            })
        })
        .collect::<Vec<_>>();

    todo!()
}

fn main() {}

This compiles successfully without disjoint captures, but with them enable gives a lifetime error:

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> src/main.rs:42:5
   |
42 |     gamemodes: &'a HashMap<&str, GameMode>,
   |     ^^^^^^^^^
   |
note: first, the lifetime cannot outlive the lifetime `'a` as defined on the function body at 41:30...
  --> src/main.rs:41:30
   |
41 | pub async fn get_replay_menu<'a>(
   |                              ^^
note: ...so that the expression is assignable
  --> src/main.rs:42:5
   |
42 |     gamemodes: &'a HashMap<&str, GameMode>,
   |     ^^^^^^^^^
   = note: expected `&HashMap<&str, GameMode>`
              found `&'a HashMap<&str, GameMode>`
note: but, the lifetime must be valid for the anonymous lifetime #1 defined on the body at 48:29...
  --> src/main.rs:48:29
   |
48 |               MenuOption::new(move |f| {
   |  _____________________________^
49 | |                 Box::pin(construct_gamestate_replay(&gamemodes[entry.as_str()], f))
50 | |             })
   | |_____________^
note: ...so that the expression is assignable
  --> src/main.rs:49:17
   |
49 |                 Box::pin(construct_gamestate_replay(&gamemodes[entry.as_str()], f))
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   = note: expected `Pin<Box<dyn Future<Output = Box<dyn GameState<'_>>>>>`
              found `Pin<Box<dyn Future<Output = Box<dyn GameState<'_>>>>>`

I would expect either it to compile successfully, or the rust_2021_incompatible_closure_captures lint to provide a suggestion to make it compile successfully.

Found in the 2021 crater run for https://crater-reports.s3.amazonaws.com/pr-87190-3/try%23a7a572ce3edd6d476191fbfe92c9c1986e009b34/gh/stefan-blair.tetrust/log.txt

(Sorry, didn't narrow the example down further, or if this is a duplicate.)

Meta

rustc --version --verbose:

rustc 1.56.0-nightly (ac50a5335 2021-08-27)
binary: rustc
commit-hash: ac50a53359328a5d7f2f558833e63d59d372e4f7
commit-date: 2021-08-27
host: x86_64-apple-darwin
release: 1.56.0-nightly
LLVM version: 13.0.0

cc @rust-lang/wg-rfc-2229

Metadata

Metadata

Assignees

Labels

A-closuresArea: Closures (`|…| { … }`)A-edition-2021Area: The 2021 editionC-bugCategory: This is a bug.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions