RustArena

Step 1 of 7

Channels: sending messages between threads

In the last module, threads only talked to main once, at the very end, through the value join returned. Often a thread needs to hand over results while it's running: a reader thread passing log lines to a parser, workers reporting finished jobs. The Rust book borrows a slogan from Go: "Do not communicate by sharing memory; instead, share memory by communicating."

A channel is a one-way pipe between threads: one end sends values, the other receives them. The standard library's channels live in std::sync::mpsc, which stands for multiple producer, single consumer: many threads may send, one receives.

use std::sync::mpsc;
use std::thread;

fn main() {
    let (tx, rx) = mpsc::channel();

    let producer = thread::spawn(move || {
        for line in ["boot ok", "disk 81% full", "backup done"] {
            tx.send(String::from(line)).unwrap();
        }
    });

    for msg in rx {
        println!("got: {msg}");
    }
    producer.join().unwrap();
    println!("channel closed");
}
got: boot ok
got: disk 81% full
got: backup done
channel closed

Piece by piece:

Other ways to receive: rx.try_recv() returns immediately (an Err if nothing is waiting), and rx.recv_timeout(duration) waits at most that long.

Sending gives the value away

send takes ownership, so once a value is sent, the sending thread can't touch it. That's what makes channels safe: two threads never use the same value at the same time.

use std::sync::mpsc;
use std::thread;

fn main() {
    let (tx, rx) = mpsc::channel();
    thread::spawn(move || {
        let msg = String::from("ready");
        tx.send(msg).unwrap();
        println!("sent {}", msg);
    });
    println!("{}", rx.recv().unwrap());
}
error[E0382]: borrow of moved value: `msg`

Common mistakes

Your task

Fill in the blanks: create the channel, and send each temperature from the producer thread. The program should print readings total: 88.

Challenge 2

Receive exactly one message: fill in the method that waits for the next message. The program should print 12 orders packed.

Challenge 3

Build a small pipeline. A worker thread goes through the raw lines, parses each one as a u32, and sends only the ones that parse over a channel. The main thread collects everything it receives into a Vec (rx.iter().collect() works) and prints:

valid: [12, 7, 30]
sum: 49

Next: Multiple producers: cloning the Sender