2026-04-19 00:32:19 +02:00

96 lines
3.0 KiB
Rust

// Two people are talking on the phone. One of them is telling a story. The
// other one is interjecting with little acknowledgments, to show their interest
// in the story.
//
// However, there is a problem. The phone connection is synchronous, so all
// the acknowledgments from the listener arrive only at the very end of the
// conversation! What the speaker and listener say should be interleaved.
//
// Let's use asynchronous programming to make the conversation more natural!
use std::time::Duration;
use tokio::sync::mpsc;
fn main() {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_time()
.build()
.unwrap();
let _guard = rt.enter();
let time_scale = Duration::from_millis(1);
let (speaker_phone, listener_phone, mut wire_tap) = start_wire_tapped_phone_call();
let speaker = async move {
for msg in SPEAKER_MESSAGES {
speaker_phone.say(msg).await;
// wait for listener to interject
wait_silently(time_scale * 2).await;
}
};
let listener = async move {
// give speaker a head-start
wait_silently(time_scale * 1).await;
for msg in LISTENER_MESSAGES {
listener_phone.say(msg).await;
// wait for speaker to continue story
wait_silently(time_scale * 2).await;
}
};
tokio::spawn(speaker);
tokio::spawn(listener);
let messages: Vec<_> = std::iter::from_fn(|| rt.block_on(wire_tap.recv())).collect();
for message in &messages {
println!("{message}");
}
let expected = SPEAKER_MESSAGES
.iter()
.zip(LISTENER_MESSAGES)
.flat_map(|(&a, &b)| [a, b]);
for (expected, message) in expected.zip(messages) {
assert_eq!(message, expected, "")
}
}
async fn wait_silently(duration: Duration) {
// TODO: The sleep function from the standard library blocks the current
// thread, preventing other async tasks from progressing. The tokio
// library, which provides our async runtime, can help:
// https://docs.rs/tokio/latest/tokio/time/fn.sleep.html
tokio::time::sleep(duration).await;
}
const SPEAKER_MESSAGES: &[&str] = &[
"> So I was walking in the park...",
"> where I met Susan by coincidence...",
"> and she was wearing a purple hat!",
];
const LISTENER_MESSAGES: &[&str] = &[
" I see. <",
" Oh, really? <",
" No way! <",
];
/// This phone is wire-tapped for testing purposes.
#[derive(Clone)]
struct Phone {
sender: mpsc::Sender<&'static str>,
}
// Create a wire-tapped phone call.
fn start_wire_tapped_phone_call() -> (Phone, Phone, mpsc::Receiver<&'static str>) {
let (sender, wire_tap) = mpsc::channel(6);
let phone = Phone { sender };
(phone.clone(), phone, wire_tap)
}
impl Phone {
/// Say something on the phone.
async fn say(&self, thing: &'static str) {
self.sender.send(thing).await.unwrap();
}
}