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:
mpsc::channel()returns a pair: theSender(by traditiontx, short for transmitter) and theReceiver(rx). The message type is inferred from what you send: hereSender<String>.tx.send(value)moves the value into the channel. It returns aResult:Errmeans the receiver is gone and nobody will ever read the message.rx.recv()waits until a message arrives and returnsOk(msg). It returnsErronce the channel is empty and every sender has been dropped.for msg in rx(orfor msg in rx.iter()) callsrecvover and over and stops at that point. Heretxwas moved into the producer, so when the producer finishes,txis dropped and the loop ends.- Messages from one sender arrive in the order they were sent (first in, first out).
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
- Using a value after sending it (
E0382above). Print or copy what you need beforesend. send(...).unwrap()in a long-running worker. If the receiver has gone away, that panics.if tx.send(x).is_err() { break; }stops the worker politely instead.- A loop over
rxthat never ends. It ends only when every sender is dropped. The next step shows the classic way to get this wrong.
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