Rust Tokio Broadcast
A broadcast channel is a channel that sends every message to every listener. A watch channel holds one shared value for many listeners.
Every listener gets a copy
One send, and each subscriber receives its own copy of the message. With mpsc, only one of them could have taken it:
let (tx, _first) = broadcast::channel::<String>(8);
let mut listener_a = tx.subscribe();
let mut listener_b = tx.subscribe();
tx.send(String::from("server restarting")).unwrap();
println!("A heard: {}", listener_a.recv().await.unwrap());
println!("B heard: {}", listener_b.recv().await.unwrap());Fall too far behind, and you skip
broadcast is a ring buffer. A receiver that falls too far behind gets a Lagged error and skips the messages it missed:
let (tx, _rx) = broadcast::channel::<i32>(2); // ring buffer holds 2
let mut slow = tx.subscribe();
tx.send(1).unwrap();
tx.send(2).unwrap();
tx.send(3).unwrap(); // message 1 is overwritten
match slow.recv().await {
Err(e) => println!("fell behind: {e}"),
Ok(n) => println!("got {n}"),
}watch keeps only the newest value
A watch channel holds one value. Send again, and the old value is simply replaced — no queue builds up:
let (tx, rx) = watch::channel(20); // current temperature
tx.send(25).unwrap();
tx.send(31).unwrap(); // 25 is overwritten, unread
println!("latest = {}", *rx.borrow());Read now with borrow, wait with changed
borrow() reads the current value at once. changed().await waits until a new value replaces it:
let (tx, mut rx) = watch::channel(0);
println!("start = {}", *rx.borrow()); // borrow() reads now, marks seen
tx.send(5).unwrap();
rx.changed().await.unwrap(); // a change since the borrow
println!("after change = {}", *rx.borrow());Example
// Cargo.toml: tokio = { version = "1", features = ["full"] }
use tokio::sync::broadcast;
#[tokio::main]
async fn main() {
let (tx, _first) = broadcast::channel::<String>(8);
let mut listener_a = tx.subscribe();
let mut listener_b = tx.subscribe();
tx.send("server restarting in 5 minutes".to_string()).unwrap();
println!("A heard: {}", listener_a.recv().await.unwrap());
println!("B heard: {}", listener_b.recv().await.unwrap());
}Key points
- Use broadcast when each listener must see all messages, like chat or live events
- Use watch for shared state that changes: a config value, a shutdown flag, a progress number
- Rule of thumb: every message matters → broadcast.
- Only the latest value matters → watch.
- Each message goes to one worker → mpsc.
In this example, one broadcast channel gets two subscribers. A single send puts the message into the channel once. Both listeners then receive their own copy of it — with mpsc, only one of them could have taken it. This is exactly the shape of a chat room or an announcement feed.
Note
broadcast receivers are made with sender.subscribe() — a receiver only sees messages sent after it subscribed.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Rust Playground
Exercise
Your app shows the current temperature. Updates arrive ten times a second, and only the newest matters. Which channel fits?