Trait bounds of type also applied to generic type parameters?

⚓ Rust    📅 2026-02-26    👤 surdeus    👁️ 1      

surdeus

I'm hitting a wall with a weird-to-me compiler error. When creating a generic axum handler function, it requires that the type parameters be Send + Sync + 'static, even in examples where they are simple associated types that never need to be sent between threads. I found a minimum reproducible example where the type parameter isn't even ever used:

use std::sync::Arc;

use axum::{Router, extract::State, response::IntoResponse, routing::get};

trait Greeter: Send + Sync {
    type GreetType;
    fn greet(&self) -> String {
        format!("Hello World!")
    }
}

struct AppState<G> {
    greeter: Arc<dyn Greeter<GreetType = G>>,
}

impl<G> Clone for AppState<G> {
    fn clone(&self) -> Self {
        Self {
            greeter: Arc::clone(&self.greeter),
        }
    }
}

async fn handler<G>(State(state): State<AppState<G>>) -> impl IntoResponse {
    state.greeter.greet()
}

struct ConcreteGreter;

impl Greeter for ConcreteGreter {
    type GreetType = i8;
}

fn router<G>() -> Router<AppState<G>> {
    Router::new().route("/", get(handler::<G>))
}

#[tokio::main]
async fn main() {
    let state = AppState {
        greeter: Arc::new(ConcreteGreter),
    };
    let app = router::<i8>().with_state(state);
    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

The compile error here is

error[E0310]: the parameter type `G` may not live long enough
  --> src/main.rs:35:5
   |
35 |     Router::new().route("/", get(reply::<G>))
   |     ^^^^^^^^^^^
   |     |
   |     the parameter type `G` must be valid for the static lifetime...
   |     ...so that the type `G` will meet its required lifetime bounds
   |
help: consider adding an explicit lifetime bound
   |
34 | fn router<G: 'static>() -> Router<AppState<G>> {
   |            +++++++++

I don't understand how the lifetime of G even matters here. It's an associated type, it's not contained in any struct, future, or function parameter and it's never held across an await point or returned. Here in this minimal example I could fix it by adding a 'static bound, but in my real life example I don't have that option because the associated type may be a reference (edit: i think that last sentence didn't make sense).

One that is never held across an await point or returned either, but the compiler doesn't seem to agree with me.

What am I missing here?

4 posts - 2 participants

Read full topic

🏷️ Rust_feed