I am in fact using async functions, so if there is a great api that uses futures I am open to this as well. Not knowing enough about async in Rust I am not sure if this is even feasible.
E.g.
let a = not_send_yet(ARequest{...});
let b = not_send_yet(AnotherRequest{...});
send_requests(vec![a,b]).await;
futures::join!(a,b);
Unfortunately that's not very typesafe. I'd prefer a different approach, e.g. using std::any. But I am a little lost on how to do that exactly.
The idea regarding async was maybe not as clear. I'll amend it with a different type I am sure I would need.
let requests_wrapper = ...
// this should save record that this request is to be sent
let a = requests_wrapper.register_request(ARequest{...});
let b = requests_wrapper.register_request(AnotherRequest{...});
// This will finally send the **one** HTTP request
requests_wrapper.send_requests().await;
futures::join!(a,b);
send_requests() would then "fill" the future. I don't know if that's possible.
What do you mean by that? The API you are calling is not very typesafe or the approach of using concrete types to interact with your API? If you refer to the latter, I'd disagree that using dynamic typing is more typesafe than using concrete types. Also, serde is designed to work with concrete types. Personally, I've never seen it being used with std::any::Any.
Your type could be a simple vector of requests. I was imagining something like this to send your HTTP request:
let requests: Vec<Request> = ...
reqwest::post("your endpoint")
.json(&requests)
.send()
.await;
What you are describing would be some sort of lazy future. Maybe there is an implementation for that out there, but I don't know one and have never needed it. It would be far easier if send_requests() creates a single future for your HTTP request that resolves with your answers. No need for every operation you put into your HTTP body to be a future. They would all resolve with the response from the http request anyway, so no need to be extra complicated here.
If I understand you correctly, what you want is, given a tuple (R1, R2) of requests, you want the API to figure out that the return type must be (R1::Response, R2::Response). Is that correct?
This way you lose the information that for RequestType1 the response is going to be ResponseType1.
And every use of any ReponseType1 would have to runtime match on the ResponseInner.
And you are totally right that framing it as a tuple makes it more accessible :face_palm:.
I thought you can match your response to your request via the tag (or position in the array). That way, if response["tag"] matches the right response type for request["tag"], you'd be good. This can be implemented easily based on the types I described above:
I have whipped up a quick example showing how to build a compile-time cons list out of pairs using both type-level and value-level recursion: Playground
This is completely type-safe in that it statically infers a "list" (nested pairs) of response types from a "list" (nested pairs) of request types, sending the requests and receiving their responses in order.
The construction and destructuring of the nested request and response tuples is a bit ugly, though. You can use a macro for abstracting it away, e.g. like this.