My wish is so simple - i just want a Hashmap of tower::Services - why is it so hard???
I am trying to build a Hashmap of tower::Service but am stuck trying to please the compiler
i started out with function that builds a tower service as below and i use impl
as below which compiles ok:
fn build_svc<T>(_val: T) -> impl Service<String, Response=String, Error=BoxError> {
let mut svc = ServiceBuilder::new();
let svc = svc.layer(TimeoutLayer::new(Duration::from_secs(100)));
// add layer specific to type T
let svc = svc.service_fn( |req: String| async move { Ok::<_, BoxError>(req) });
svc
}
because build_svc()
is generic over T, i want to keep the return type "dynamic"
then i tried to define a struct that will hold the Hashmap of Services, as below (i had to fix compile errors - A & B):
type MyFuture = Pin<Box<Future<Output=Result<String, BoxError>> + Send + 'static>>;
type MyMap =HashMap<String, Box<dyn Service<String,Response=String, Error=BoxError, Future=MyFuture>>>; // compile ok
struct ServiceMap {
// svcs: HashMap<String, impl Service<String>>, // compile error (A)
// svcs: HashMap<String, Box<dyn Service<String,Response=_, Error=_, Future=_>>>, // compile error (B)
svcs: MyMap,
}
Initially, i tried svcs: HashMap<String, impl Service<String>>
, which gave me compile error (A), i.e. impl trait
not allowed
So, i tried `svcs: HashMap<String, Box<dyn Service<String,Response=, Error=, Future=>>> in an effort to not constraint the associated types.
Nope, the compiler does not like this and insists i specify all the types, complaining that " not allowed in type signatures"
I gave in and specified all the Associated Types (type MyMap above). Now the compiler is happy
Compile Error A
error[E0562]: `impl Trait` not allowed outside of function and method return types
--> src/tryimplvsdyn.rs:14:26
|
14 | svcs: HashMap<String, impl Service<String>>,
| ^^^^^^^^^^^^^^^^^^^^
Compile Error B
--> src/tryimplvsdyn.rs:15:54
|
15 | svcs: HashMap<String, dyn Service<String,Response=_, Error=_, Future=_>>, // compile error (A)
| ^ ^ ^ not allowed in type signatures
| | |
| | not allowed in type signatures
| not allowed in type signatures
|
help: use type parameters instead
|
13 | struct ServiceMap<T> {
15 | svcs: HashMap<String, dyn Service<String,Response=T, Error=T, Future=T>>,
|
Great, compiler happy ...
Subsequently, i tried using the HashMap (struct ServiceMap
) with fn build_svc()
in the main code below:
#[tokio::main]
async fn main() {
let svcs = MyMap::new();
svcs.insert("test".to_string(), Box::new(build_svc())); // compile error (C)
}
Compile Error C
error[E0271]: type mismatch resolving `<impl Service<String> as Service<String>>::Future == Pin<Box<(dyn std::future::Future<Output = Result<String, Box<(dyn std::error::Error + Send + Sync + 'static)>>> + 'static)>>`
--> src/tryimplvsdyn.rs:30:37
|
8 | fn build_svc<T>(val: T) -> impl Service<String, Response=String, Error=BoxError> { // compiles error (D)
| ----------------------------------------------------- the found opaque type
...
30 | svcs.insert("test".to_string(), Box::new(build_svc(true))); // compile error (C)
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `Pin`, found associated type
|
= note: expected struct `Pin<Box<(dyn std::future::Future<Output = Result<String, Box<(dyn std::error::Error + Send + Sync + 'static)>>> + 'static)>>`
found associated type `<impl Service<String> as Service<String>>::Future`
= note: required for the cast to the object type `dyn Service<String, Error = Box<(dyn std::error::Error + Send + Sync + 'static)>, Future = Pin<Box<(dyn std::future::Future<Output = Result<String, Box<(dyn std::error::Error + Send + Sync + 'static)>>> + 'static)>>, Response = String>`
help: consider constraining the associated type `<impl Service<String> as Service<String>>::Future` to `Pin<Box<(dyn std::future::Future<Output = Result<String, Box<(dyn std::error::Error + Send + Sync + 'static)>>> + 'static)>>`
|
8 | fn build_svc<T>(val: T) -> impl Service<String, Response=String, Error=BoxError, Future = Pin<Box<(dyn std::future::Future<Output = Result<String, Box<(dyn std::error::Error + Send + Sync + 'static)>>> + 'static)>>> { // compiles error (D)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Again dear compiler is unhappy, now complaining "type mismatch" because the tower::Service return an opaque type?? Something about the Service::Future doesn't natch as it found an Associated type Service::Future
instead of MyFuture
??
So i tried changing the build_svc()
to return Box<dyn Service>
instead, again compiler insists i give all Associated types - which i obliged:
fn build_svc_b<T>(val: T) -> Box< dyn Service<String, Response=String, Error=BoxError, Future=MyFuture>> {
let mut svc = ServiceBuilder::new();
let svc = svc.layer(TimeoutLayer::new(Duration::from_secs(100)));
// add as layer a Service specific to generic type T
let svc = svc.service_fn( |req: String| async move { Ok::<_, BoxError>(req) });
Box::new(svc)
}
and still the compiler isn't happy ...
Compile Error D
error[E0271]: type mismatch resolving `<tower::timeout::Timeout<ServiceFn<[closure@src/tryimplvsdyn.rs:14:31: 14:82]>> as Service<String>>::Future == Pin<Box<(dyn std::future::Future<Output = Result<String, Box<(dyn std::error::Error + Send + Sync + 'static)>>> + 'static)>>`
--> src/tryimplvsdyn.rs:9:32
|
9 | fn build_svc(timeout: bool) -> impl Service<String, Response=String, Error=BoxError, Future=MyFuture> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `Pin`, found struct `tower::timeout::future::ResponseFuture`
|
= note: expected struct `Pin<Box<(dyn std::future::Future<Output = Result<String, Box<(dyn std::error::Error + Send + Sync + 'static)>>> + 'static)>>`
found struct `tower::timeout::future::ResponseFuture<_>`
At this point, i conclude there's no way to make the compiler happy! I can't have a HashMap with impl
and i need to give all associated types if i use dyn
which suddenly becomes not-dynamic anymore and signatures don't match. How can i fix this??
My wish is so simple - i just want a Hashmap of tower::Services - why is it so hard???