rust-lang / rust-lang/rust

error: lifetime bound not satisfied

Open
#146,110 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-diagnostics fixed-by-higher-ranked-assumptions T-compiler
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

Code
use askama::Template;
use askama_web::WebTemplate;
use async_trait::async_trait;
use axum::Router;
use axum::extract::{Path, Request, State, FromRequestParts};
use axum::response::{IntoResponse, Response};
use axum::routing::get;
use snafu::prelude::*;

#[derive(Clone, Debug)]
pub struct AppState;

impl AppState {
    pub async fn context(&self) -> Result<AppStateContext, AppStateError> {
        // We simulate a global non-recoverable app error
        if Self::random_failure() {
            return Err(AppStateError::RandomFail);
        }

        Ok(AppStateContext {
            errors: vec!(),
            foo: Self::foo().to_string(),
        })
    }
    
    pub fn foo() -> &'static str {
        "foo"
    }

    // A runtime failure that affects the application globally (non-recoverable).
    pub fn random_failure() -> bool {
        rand::random_bool(0.5)
    }

    // A runtime failure which the route will decide to ignore or not.
    pub async fn db_operation() -> Result<(), AppStateError> {
        if Self::random_failure() {
            Err(AppStateError::DBFail)
        } else {
            Ok(())
        }
    }
}

#[derive(Debug)]
pub struct AppStateContext {
    pub errors: Vec<AppStateError>,
    pub foo: String,
}

#[derive(Debug, Snafu)]
pub enum AppStateError {
    #[snafu(display("Connection to the database failed."))]
    DBFail,
    #[snafu(display("Random global failure."))]
    RandomFail,
    #[snafu(display("Other error: {source}"))]
    Other {
        source: Box<dyn snafu::Error + Send + Sync + 'static>
    },
}

impl IntoResponse for AppStateError {
    fn into_response(self) -> Response {
        let view = AppStateErrorView { error: self };
        view.into_response()
    }
}

/// Helper struct because Snafu and Template have conflicting derives
/// (both implement Display).
/// 
/// ```askama
/// ERROR:: {{ error }}
/// ```
#[derive(Debug, Template, WebTemplate)]
#[template(ext = "html", in_doc = true)]
pub struct AppStateErrorView {
    pub error: AppStateError,
}

#[derive(Debug)]
pub struct AppRouteView<T> {
    pub route: T,
    pub state: AppStateContext,
}

impl<T: IntoAppRouteView> AppRouteView<T> {
    pub async fn new(req: Request, state: AppState) -> Result<impl IntoResponse, AppStateError> {
        let context = state.context().await?;
        Ok(T::from_app_request(req, state, context).await?)
    }
}

#[async_trait]
pub trait IntoAppRouteView {
    async fn from_app_request(req: Request, state: AppState, mut context: AppStateContext) -> Result<impl IntoResponse, AppStateError>;
}

#[derive(Debug)]
pub struct IndexRouteContext {
    pub id: usize,
}

#[async_trait]
impl IntoAppRouteView for IndexRouteContext {
    async fn from_app_request(req: Request, state: AppState, mut context: AppStateContext) -> Result<impl IntoResponse, AppStateError> {
        
        // Simulate an operation that fails without affecting global operations
        if rand::random_bool(0.5) {
            context.errors.push(AppStateError::DBFail);
        }

        let (mut parts, body) = req.into_parts();
        // This is a route-specific operation that can fail and affects global operations
        let Path(id) = Path::<usize>::from_request_parts(&mut parts, &state).await.boxed().context(OtherSnafu)?;
        let _req = Request::from_parts(parts, body);

        Ok(IndexRouteView {
            ctx: AppRouteView {
                route: IndexRouteContext {
                    id,
                },
                state: context,
            }
        })
    }
}

/// ```askama 
/// <div>Hello, user n° {{ ctx.route.id }}!</div>
/// ```
#[derive(Debug, WebTemplate, Template)]
#[template(ext = "html", in_doc = true)]
pub struct IndexRouteView {
    ctx: AppRouteView<IndexRouteContext>,
}

// #[axum::debug_handler]
pub async fn index_route(State(app_state): State<AppState>, req: Request) -> Result<impl IntoResponse, AppStateError> {
    Ok(AppRouteView::<IndexRouteContext>::new(req, app_state).await?)
}

#[tokio::main(flavor = "current_thread")]
async fn main() {
    let app = Router::new()
        .route("/{id}", get(index_route))
        .with_state(AppState);
    
    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}
Current output
$ cargo run
   Compiling axum-test-askama v0.1.0 (/home/user/Prog/angrynode/axum-test-askama)
error: lifetime bound not satisfied
   --> src/main.rs:155:25
    |
155 |         .route("/{id}", get(index_route))
    |                         ^^^^^^^^^^^^^^^^
    |
    = note: this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)

error: could not compile `axum-test-askama` (bin "axum-test-askama") due to 1 previous error
Desired output
???
Rationale and extra context

This problem is similar to #100013, #143971 (etc) but compared with other tickets i found the difference here is i have zero explicit lifetime in my code (before macro expansion) and the rustc error is very mysterious: which lifetime bound is not satisfied? satisfied by what?

Compiling on nightly with -Zhigher-ranked-assumptions works just fine so maybe we don't need to improve diagnostic output on stable if stabilization for this feature is coming soon?

Other cases

Rust Version
rustc 1.89.0 (29483883e 2025-08-04)
binary: rustc
commit-hash: 29483883eed69d5fb4db01964cdf2af4d86e9cb2
commit-date: 2025-08-04
host: x86_64-unknown-linux-gnu
release: 1.89.0
LLVM version: 20.1.7
Anything else?

No response

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

Start by reproducing the failure with cargo run from the provided src/main.rs, especially the get(index_route) call at line 155. Compare stable rustc 1.89.0 with nightly using -Zhigher-ranked-assumptions, then read the related Rust issues #100013 and #143971. Done means the stable compiler provides a clear diagnostic or correctly accepts this reduced case.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.