1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2025-01-09 09:11:50 +02:00

Simplify the select! example (#2154)

@mgeisler PTAL, as I believe you were the one to suggest this change in
#1536.

This leaves some detail about `select!` out, but that might be just as
well. Some of that detail is addressed in the `pin!` pitfall, later.
This commit is contained in:
Dustin J. Mitchell 2024-08-09 08:21:00 -04:00 committed by GitHub
parent cae5cb05e8
commit 76857d76a1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -16,63 +16,33 @@ the resulting variables. The `statement` result becomes the result of the
`select!` macro.
```rust,editable,compile_fail
use tokio::sync::mpsc::{self, Receiver};
use tokio::sync::mpsc;
use tokio::time::{sleep, Duration};
#[derive(Debug, PartialEq)]
enum Animal {
Cat { name: String },
Dog { name: String },
}
async fn first_animal_to_finish_race(
mut cat_rcv: Receiver<String>,
mut dog_rcv: Receiver<String>,
) -> Option<Animal> {
tokio::select! {
cat_name = cat_rcv.recv() => Some(Animal::Cat { name: cat_name? }),
dog_name = dog_rcv.recv() => Some(Animal::Dog { name: dog_name? })
}
}
#[tokio::main]
async fn main() {
let (cat_sender, cat_receiver) = mpsc::channel(32);
let (dog_sender, dog_receiver) = mpsc::channel(32);
tokio::spawn(async move {
sleep(Duration::from_millis(500)).await;
cat_sender.send(String::from("Felix")).await.expect("Failed to send cat.");
});
tokio::spawn(async move {
sleep(Duration::from_millis(50)).await;
dog_sender.send(String::from("Rex")).await.expect("Failed to send dog.");
let (tx, mut rx) = mpsc::channel(32);
let listener = tokio::spawn(async move {
tokio::select! {
Some(msg) = rx.recv() => println!("got: {msg}"),
_ = sleep(Duration::from_millis(50)) => println!("timeout"),
};
});
sleep(Duration::from_millis(10)).await;
tx.send(String::from("Hello!")).await.expect("Failed to send greeting");
let winner = first_animal_to_finish_race(cat_receiver, dog_receiver)
.await
.expect("Failed to receive winner");
println!("Winner is {winner:?}");
listener.await.expect("Listener failed");
}
```
<details>
- In this example, we have a race between a cat and a dog.
`first_animal_to_finish_race` listens to both channels and will pick whichever
arrives first. Since the dog takes 50ms, it wins against the cat that take
500ms.
- The `listener` async block here is a common form: wait for some async event,
or for a timeout. Change the `sleep` to sleep longer to see it fail. Why does
the `send` also fail in this situation?
- You can use `oneshot` channels in this example as the channels are supposed to
receive only one `send`.
- Try adding a deadline to the race, demonstrating selecting different sorts of
futures.
- Note that `select!` drops unmatched branches, which cancels their futures. It
is easiest to use when every execution of `select!` creates new futures.
- An alternative is to pass `&mut future` instead of the future itself, but
this can lead to issues, further discussed in the pinning slide.
- `select!` is also often used in a loop in "actor" architectures, where a task
reacts to events in a loop. That has some pitfalls, which will be discussed in
the next segment.
</details>