Skip to content

hidden type for impl ... captures lifetime that does not appear in bounds #147529

@eopb

Description

@eopb

Code

This is how far I've got trying to minimise things:

#![allow(unreachable_code, unused_variables, dead_code)]

use std::collections::HashMap;
use std::pin::Pin;
use std::sync::Arc;

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

struct BrokerAction;
struct HandlerError;

impl From<()> for BrokerAction {
    fn from((): ()) -> Self {
        BrokerAction
    }
}

impl From<Error> for HandlerError {
    fn from(Error: Error) -> Self {
        HandlerError
    }
}

trait AsyncClosure<'a, Context>: Send + Sync + 'static {
    type Output: Future<Output = Result<Self::Action, Self::Err>> + Send + 'a;
    type Action: Into<BrokerAction> + 'static;
    type Err: Into<HandlerError> + 'static;
    fn call(&'a self, incoming: Incoming<'a, Context>) -> Self::Output;
}

impl<'a, F, Fut, Err, Context, Action> AsyncClosure<'a, Context> for F
where
    Context: 'static,
    F: Send + Sync + 'static + Fn(Incoming<'a, Context>) -> Fut,
    Fut: Future<Output = Result<Action, Err>> + Send + 'a,
    Action: Into<BrokerAction> + 'static,
    Err: Into<HandlerError> + 'static,
{
    type Err = Err;
    type Action = Action;
    type Output = Fut;

    fn call(&'a self, incoming: Incoming<'a, Context>) -> Self::Output {
        (self)(incoming)
    }
}

fn handler(
    handlers: impl IntoIterator<Item = (&'static str, MessageHandler)>,
) -> impl 'static + for<'a> AsyncClosure<'a, ApplicationContext> {
    let handlers: Arc<HashMap<&str, MessageHandler>> = Arc::new(handlers.into_iter().collect());

    move |incoming: Incoming<'_, ApplicationContext>| {
        let handlers = Arc::clone(&handlers);

        let kind: &str = todo!();

        let content_type = todo!();

        let data = incoming.message.data.clone();
        let context = incoming.context;

        async move {
            let kind = kind;
            let content_type = content_type;

            let handler = handlers.get(kind).unwrap();

            handler(&data, context).await
        }
    }
}

type MessageHandler = Box<
    dyn for<'a> Fn(&'a [u8], Arc<ApplicationContext>) -> BoxFuture<'a, Result<(), Error>>
        + Sync
        + Send,
>;

struct ApplicationContext;
struct Error;

struct Incoming<'d, C> {
    context: Arc<C>,
    message: &'d Delivery,
}

#[derive(Debug, PartialEq)]
struct Delivery {
    data: Vec<u8>,
}

This code compiles on stable, but fails on nightly and beta.

Version it worked on

The most recent I've tested was: cargo 1.90.0 (840b83a10 2025-07-30):

: cargo +stable c
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.09s

Version with regression

The most recent I've tested was: cargo 1.92.0-nightly (801d9b498 2025-10-04):

: cargo +nightly-2025-10-08 c
    Checking regress v0.1.0 (/private/var/folders/9d/tqvdlqrd6fz7t8746q0jdjzr0000gp/T/tmp.xBu0LdwlmB/regress)
error[E0700]: hidden type for `impl AsyncClosure<'a, ...>` captures lifetime that does not appear in bounds
  --> src/lib.rs:53:5
   |
50 |   ) -> impl 'static + for<'a> AsyncClosure<'a, ApplicationContext> {
   |        ----------------------------------------------------------- opaque type defined here
...
53 | /     move |incoming: Incoming<'_, ApplicationContext>| {
54 | |         let handlers = Arc::clone(&handlers);
55 | |
56 | |         let kind: &str = todo!();
...  |
71 | |     }
   | |_____^
   |
   = note: hidden type `{closure@src/lib.rs:53:5: 53:54}` captures the static lifetime
   = note: the full name for the type has been written to '/private/var/folders/9d/tqvdlqrd6fz7t8746q0jdjzr0000gp/T/tmp.xBu0LdwlmB/regress/target/debug/deps/regress-494a0fda5fa8247a.long-type-5522052737140525087.txt'
   = note: consider using `--verbose` to print the full type name to the console

For more information about this error, try `rustc --explain E0700`.
error: could not compile `regress` (lib) due to 1 previous error

cc @lcnr since we spoke about this at EuroRust

Metadata

Metadata

Assignees

Labels

C-bugCategory: This is a bug.P-criticalCritical priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.regression-from-stable-to-betaPerformance or correctness regression from stable to beta.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions