I implemented the tonic helloworld tutorial. I then tried to change the client code so that I could send multiple requests before awaiting any.
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let num_requests = 10;
let mut client = GreeterClient::connect("http://[::1]:50051").await?;
let mut responses = Vec::with_capacity(num_requests);
for _ in 0..num_requests {
let request = tonic::Request::new(HelloRequest {
name: "Tonic".into(),
});
responses.push(client.say_hello(request));
}
for resp in responses {
assert!(resp.await.is_ok());
}
Ok(())
}
This results in a compilation error:
error[E0499]: cannot borrow `client` as mutable more than once at a time
--> src/client.rs:19:24
|
19 | responses.push(client.say_hello(request));
| ^^^^^^ mutable borrow starts here in previous iteration of loop
Does that mean 'client.say_hello()' returns a type which still references client, and therefore I can't make another call to 'say_hello', which itself requires '&mut self'? Is there a way to continue to make requests before calling to 'await'?
From the Tonic documentation:
Sending a request on a channel requires a
&mut self
and thus can only send one request in flight. This is intentional and is required to follow theService
contract from thetower
library which this channel implementation is built on top of.
...
To work around this and to ease the use of the channel,Channel
provides aClone
implementation that is cheap. This is because at the very top level the channel is backed by atower_buffer::Buffer
which runs the connection in a background task and provides ampsc
channel interface. Due to this cloning theChannel
type is cheap and encouraged.
Therefore, you can clone the client for each concurrent request you make. This eliminates the possibility of a single client being mutably borrowed more than once at any given time, so the borrow checker is appeased.
let num_requests = 10;
let client = GreeterClient::connect("http://[::1]:50051").await?;
let mut responses = Vec::with_capacity(num_requests);
for _ in 0..num_requests {
let mut client = client.clone();
let request = tonic::Request::new(HelloRequest {
name: "Tonic".into(),
});
responses.push(tokio::spawn(async move {
client.say_hello(request).await
}));
}
for resp in responses {
let resp = resp.await;
assert!(resp.is_ok());
assert!(resp.unwrap().is_ok());
}