We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Fearless Concurrency: Threads, Channels, Shared State step 3 of 24
JoinHandle, panics and thread::Result
Run one thread per input and report what happened to each, in input order.
pub fn survive_panics(inputs: Vec<i64>) -> Vec<String>
Each worker takes its x and:
-
x > 0— returnsx * 2, reported as"ok:<n>"; -
x < 0—panic!("bad {x}"), reported as"err:bad -2"; -
x == 0—panic!("zero"), reported as"err:zero".
So [1, -2, 3] produces ["ok:2", "err:bad -2", "ok:6"]. A panicking
worker must not stop the others, and must not stop the function.
What a panic in a thread actually does
Most languages pick one of two behaviours: the panic tears down the process,
or it vanishes silently. Rust does neither. A panic unwinds that thread
only, the thread dies, and the panic value is stored in the handle. Every
other thread carries on. main is unaffected until it joins.
You collect it here:
pub fn join(self) -> std::thread::Result<T>
which is
type Result<T> = std::result::Result<T, Box<dyn Any + Send + 'static>>;
That error type is the awkward part, and it is honest about the situation: a
panic payload can be any value (panic_any will take an arbitrary type), so
std cannot promise you a string. It hands you a box and lets you ask.
Which is why the starter does not compile:
error[E0277]: `Box<dyn Any + Send>` doesn't implement `std::fmt::Display`
You cannot print it. You must downcast it.
Downcast order, and why this problem has two panic forms
Box<dyn Any> gives you downcast_ref::<T>() -> Option<&T>: a checked
question, “is the value inside actually a T?”. The catch is knowing which
T to ask for, and the answer depends on how the panic was written:
| panic | payload type |
|---|---|
panic!("zero") — a literal, no formatting |
&'static str |
panic!("bad {x}") — formatted |
String |
A single format argument changes the type of the payload. This is not a
detail you can guess, and it is exactly why the two cases exist in this
problem: a solution that handles only one of them passes half the tests.
Ask for &str first, then String, and keep a fallback arm for anything else
— panic_any(42) is legal and someone’s dependency will do it eventually.
Note downcast_ref::<&str>() gives you an Option<&&str>. A double
reference, because the payload is a &str and you asked for a reference to
it. That is correct, not a mistake.
Expect noise on stderr
The default panic hook prints thread '<unnamed>' panicked at ... to stderr
for every panic, so a successful run of this problem is loud. The grader
reads stdout and ignores stderr, so that is cosmetic. In real code you would
reach for std::panic::set_hook to quieten expected panics — but a hook is a
process-global, so installing one from a library function is rude, and you
should not do it here.
The honest caveat
All of this depends on panic = "unwind", which is the default. A build
configured with panic = "abort" kills the process on the first panic and
there is no Err to inspect, because there is no unwinding. Recovering from
panics is a property of the build profile, not a guarantee of the language.
And to be clear about design: this is not how you handle expected failure
in Rust. Expected failure is Result. Catching panics is for supervision
boundaries — a thread pool that must not die because one job was buggy. If you
find yourself panicking in order to join it back, use Result instead.
Loading visualization…
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.