I'm trying to route a handler (get_request
) with 2 generics (M: ApiTrait, C: ClientTrait
)
let mut app = Router::new();
app = app.route(
"/model",
get(get_request),
);
pub async fn get_request<M: ApiTrait, C: ClientTrait>(
params: Query<RequestParams>,
client_opt: Option<Extension<C>>,
State(api): State<Arc<M>>,
) -> Result<Response, ApiError> { ... }
but got errors:
error[E0283]: type annotations needed
--> src/api/mod.rs:327:17
|
327 | get(get_request),
| ^^^^^^^^^^^ cannot infer type of the type parameter `C` declared on the function `get_request`
|
= note: cannot satisfy `_: ClientTrait`
= help: the following types implement trait `ClientTrait`:
Client
MockClient
note: required by a bound in `get_request`
--> src/api/mod.rs:108:45
|
108 | pub async fn get_request<M: ApiTrait, C: ClientTrait>(
| ^^^^^^^^^^^ required by this bound in `get_request`
help: consider specifying the generic arguments
|
327 | get(get_request::<S, C>),
| ++++++++
```.
To resolve it, I need to specify one of the generics with
get(get_request::<_, Client>)
instead of
get(get_request)
, despite that I want to keep it flexible so that I can use MockClient
during test.
I have 2 questions:
It is not about the number of generics, it is about whether the generic type parameters can be deduced from the calling context.
The M
parameter can be deduced because the Router
is strongly-typed with the state. Presumably you have a .with_state()
or other annotation that describes the router as being Router<Arc<MyState>>
where M
would be deduced as MyState
.
The C
parameter cannot be deduced because there is nothing to deduce it from. Extension
s aren't bound to the Router
type, so it can't get it from there. The only constraint is that C: ClientTrait
; how should the .route()
call for the handler know to pick the real client or a mock client (or something else entirely)?
You don't really have any options; the type cannot be deduced by the context so it must be specified.
If you can somehow merge the ClientTrait
object onto your state type, that could be a solution that would avoid explicit annotations. Otherwise...
If you have a lot of handlers that need a ClientTrait
like this, you could wrap it all function that does the router setup with a single C
generic like this:
fn make_router<C: ClientTrait + Clone + Send + Sync + 'static>() -> Router<Arc<MyState>> {
let mut app = Router::new();
app = app.route("/model", get(get_request::<_, C>));
app
}
It still requires annotations, but by "bubbling it up" you'd only have to specify what C
is once for your primary code and testing code.