Broadly speaking, the general consensus on Futures (and Streams) in Rust is that they must be "driven to completion" to perform work, and that, unlike JavaScript, they are not executed eagerly.
I have made an example below of a client that returns a Future and a Stream, with questions on at what point a request should actually be transmitted out over the internet (independent on when we begin to try to receive/poll the response).
I would like some clarification on whether the below API behaves correctly, since I have a colleague that disagrees with me. More importantly than me "being right", if possible, a justification for why submission should occur only after a poll would be appreciated.
Please view Question 1 an Question 2 in the implementation comments.
/// Fetches data about a person's programming preferences via internet
struct ProgrammingPreferencesClient {
// the client itself, upon receiving a request, sends it to a
// background thread that owns a TCP connection and
// is the actual thing transmitting and receiving over the internet
sender_to_background_io_multiplexer_thread: mpsc::UnboundedSender<_>}
}
impl ProgrammingPreferencesClient {
fn get_favorite_programming_language_for_person(&self, name: String) -> impl Future<Output=String> {
let (tx_from_background, rx_of_background_result) = oneshot::channel();
let sender = self.sender_to_background_io_multiplexer_thread.clone();
async move {
// !!!!! QUESTION 1:
// Should the `sender.sender` below occur prior to the async move? If that is the case, then
// the outgoing request will be eagerly submitted to the IO thread and set out via the internet
// even though no polling on the future has occurred yet.
// I believe the answer should be no, that the correct thing is to submit the outgoing
// request lazily, only after the first poll.
// submit the request payload to background thread, as well as a way for it
// to communicate the result to this function
let _ = sender.send((name, tx_from_background));
// oneshot's receiver implements future
rx_of_background_result.await.unwrap()
}
}
// given a person's name, return their favorite libraries
fn get_favorite_programming_libraries_for_person(&self, name: String) -> impl Stream<Item=String> {
// ...........
}
}
async fn main() {
let client: ProgrammingPreferencesClient = instantiate_my_client();
let fut = client.get_favorite_programming_language_for_person("Ada Lovelace".to_string());
// Question 1:
// at this point, no network IO, not even on the background thread should have been submitted, correct?
let result = fut.await;
// now, due to .await beginning the poll, the client should have both Submitted info and Received info over the network.
assert_eq!(&result, "Rust");
// Question 2:
// if the answer to Question 1 is that absolutely no network IO should have been submitted prior to poll,
// then does that also apply to stream?
let stream = client.get_favorite_programming_libraries_for_person("Ada Lovelace");
let mut stream = std::pin::pin!(stream);
// Question 2 continued:
// would the initial submission of this request over the internet only occur on the first poll_next?
while let Some(item) = stream.next().await {
println!("{item}");
}
}