Express trait bound for associated type

I have a very simple example axum project:

use axum::{Extension, Router};
use std::sync::Arc;

struct State;

#[tokio::main]
async fn main() {
    let shared_state = Arc::new(State);
    let app = Router::new().layer(Extension(shared_state));

    axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
        .serve(app.into_make_service())
        .await
        .unwrap();
}

Now I want to refactor out the layer into a function. Something like that:

use axum::{Extension, Router};
use std::sync::Arc;
use tower::Layer;

struct State;

#[tokio::main]
async fn main() {
    let app = Router::new().layer(get_layer());

    axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
        .serve(app.into_make_service())
        .await
        .unwrap();
}

fn get_layer<S>() -> impl Layer<S> {
    let shared_state = Arc::new(State);
    Extension(shared_state)
}

Now I get a lot of errors like this:

the trait bound `<impl Layer<Route<_>> as Layer<Route<_>>>::Service: Service<Request<_>>` is not satisfied

I think I understand the error message. It wants me to specify the trait bounds for the associated type of Layer. But I just don't know how to express it in Rust.

Can you give me a hint in the right direction?

Yeah, pretty much. Although I would phrase it as the compiler wanting you to prove the requirements of the layer() method are satisfied, and that method requires you to return some sort of Layer which chains correctly (think of layers as chains of middleware where the output from one layer is the input to the next, and we represent these outputs via associated types).

I would look for the Layer impl under the axum::Extension type's docs to see under what conditions Extension implements Layer<S>.

You could also add a where clause which literally says what you are trying to prove to the type system, for example something like where Extension<State>: Layer<S> is a valid where clause if you just want to make sure the -> impl Layer<S> return type is satisfied. I normally think of where clauses as "equations", and from that standpoint it makes sense that our "variable", S, can appear as part of the right hand side.

I guess you could also return an Extension<Arc<State>> and that would satisfy the compiler (assuming Extension<Arc<State>> is a valid layer to begin with), but that's the boring solution.

Thank you for your answer, I will try to get into it later, I am in a hurry now.

The important point I forgot in my question is that in my real project the return type (that is just Extension here) is a huge nested type. I just want to contain the mess :smiley:

1 Like