mirror of
https://github.com/google/comprehensive-rust.git
synced 2025-01-26 06:55:40 +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:
parent
cae5cb05e8
commit
76857d76a1
@ -16,63 +16,33 @@ the resulting variables. The `statement` result becomes the result of the
|
|||||||
`select!` macro.
|
`select!` macro.
|
||||||
|
|
||||||
```rust,editable,compile_fail
|
```rust,editable,compile_fail
|
||||||
use tokio::sync::mpsc::{self, Receiver};
|
use tokio::sync::mpsc;
|
||||||
use tokio::time::{sleep, Duration};
|
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]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let (cat_sender, cat_receiver) = mpsc::channel(32);
|
let (tx, mut rx) = mpsc::channel(32);
|
||||||
let (dog_sender, dog_receiver) = mpsc::channel(32);
|
let listener = tokio::spawn(async move {
|
||||||
tokio::spawn(async move {
|
tokio::select! {
|
||||||
sleep(Duration::from_millis(500)).await;
|
Some(msg) = rx.recv() => println!("got: {msg}"),
|
||||||
cat_sender.send(String::from("Felix")).await.expect("Failed to send cat.");
|
_ = sleep(Duration::from_millis(50)) => println!("timeout"),
|
||||||
});
|
};
|
||||||
tokio::spawn(async move {
|
|
||||||
sleep(Duration::from_millis(50)).await;
|
|
||||||
dog_sender.send(String::from("Rex")).await.expect("Failed to send dog.");
|
|
||||||
});
|
});
|
||||||
|
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)
|
listener.await.expect("Listener failed");
|
||||||
.await
|
|
||||||
.expect("Failed to receive winner");
|
|
||||||
|
|
||||||
println!("Winner is {winner:?}");
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
|
||||||
- In this example, we have a race between a cat and a dog.
|
- The `listener` async block here is a common form: wait for some async event,
|
||||||
`first_animal_to_finish_race` listens to both channels and will pick whichever
|
or for a timeout. Change the `sleep` to sleep longer to see it fail. Why does
|
||||||
arrives first. Since the dog takes 50ms, it wins against the cat that take
|
the `send` also fail in this situation?
|
||||||
500ms.
|
|
||||||
|
|
||||||
- You can use `oneshot` channels in this example as the channels are supposed to
|
- `select!` is also often used in a loop in "actor" architectures, where a task
|
||||||
receive only one `send`.
|
reacts to events in a loop. That has some pitfalls, which will be discussed in
|
||||||
|
the next segment.
|
||||||
- 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.
|
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user