🎓 A1Learner
🔍
0 of 45 lessons completed

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?