We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Fearless Concurrency: Threads, Channels, Shared State step 16 of 24
mpsc channels: message passing basics
Repair a function that gathers results from several worker threads:
pub fn fan_in(values: Vec<i64>) -> Vec<i64>
Start one worker per input value. Each worker doubles its value and sends
one result through the same channel. Receive exactly values.len() results,
join the workers, sort the results in ascending order and return them.
Preserve duplicates. Empty input returns an empty vector.
| Input | Result |
|---|---|
[] |
[] |
[7] |
[14] |
[3, -1, 3] |
[-2, 6, 6] |
Inputs in this exercise are small enough to use one thread per element,
and every doubled value fits in i64. This is a channel exercise, not a
thread-pool implementation or an overflow-handling task.
The starter already receives, joins and sorts. Fix how it gives each worker
a sender. Keep the counted recv() loop for this exercise.
Clone the handle before moving it into a worker
mpsc means multi-producer, single-consumer. Cloning a Sender creates
another handle to the same channel; it does not create a separate queue or
duplicate any messages. The receiver does not implement Clone.
The starter moves the original tx into the first worker closure. Another
loop iteration would try to use that moved sender, producing E0382. Give
each worker its own handle before creating the move closure:
let tx = tx.clone();
handles.push(thread::spawn(move || {
tx.send(v * 2).expect("receiver is alive");
}));
The inner binding shadows the parent handle for this iteration. The worker owns the clone; the parent still has the original for subsequent clones.
Sending takes a message by value. A non-Copy value such as a String is
moved, while the i64 used here is copied. Sending an Arc can also leave
shared ownership of its referent, so channels do not inherently eliminate
shared state. Here each worker only needs its value and sender handle.
Receive a known number of messages
Different workers may send in different orders. Sorting makes the returned vector independent of that arrival order.
recv() waits when no message is available and a sender is still connected.
It returns an error once all senders are gone and queued messages have been
drained. Already-buffered messages can still be received after the last
sender is dropped.
The counted loop finishes after n successful receives because this task
creates n workers, each expected to send once. Receiving those messages
and joining the threads serve different purposes: join also waits for
each worker to finish and reports a worker panic.
Check your understanding Does keeping the parent's original sender prevent the counted loop from finishing? Show answer Hide answer
No. The loop stops after the expected number of messages; it does not wait for channel disconnection.
Iterating with for value in rx instead keeps receiving until the channel
is disconnected and drained. If the parent retains its sender while waiting,
the iterator waits for more messages after the workers finish. Dropping the
parent's sender and letting all worker senders drop allows that style of
loop to terminate.
Counting is a protocol assumption, not a general guarantee of termination.
If a worker fails before sending, the parent can wait indefinitely because
it still owns a sender. The later join cannot report that panic while
the parent is blocked in recv. Production code needs a failure strategy,
such as closing unused senders and handling disconnection, explicit result
messages or timeouts.
Related channel operations
sendreturns the unsent message insideSendError<T>if the receiver is gone. A successful send does not guarantee the receiver will process it.try_recvreturns immediately. It distinguishes an empty connected channel from a disconnected channel with no remaining messages.recv_timeoutwaits for at most the requested duration and distinguishes a timeout from disconnection.
Channel handles can cross threads when their message type satisfies the
required Send bound. Sending handles and sharing data still have type and
lifetime requirements; cloning a sender does not bypass them.
Submissions must compile, pass the cases and pass the Rust Clippy gate.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Rust reference solution
Sign in to track your attempts toward unlocking the reference solution.