
Rust interviews have an unusual problem: the compiler has already done the part that interviews traditionally test.
A candidate cannot hand you code with a use-after-free in it. They cannot race two threads over the same mutable value by accident. The class of bug that whiteboard questions in C or C++ were designed to catch does not survive compilation, and asking somebody to explain lifetimes mostly measures how recently they read the book.
What the compiler cannot check is everything else, and that is where Rust services actually fall over.
unwrap or expect in a request path, on input that turns out to be user-controlled.Four tasks, one service, an afternoon of setup.
The setup. A handler that parses something from the request and unwraps the result. Normal traffic is fine. One malformed input, which arrives occasionally from a real client, panics the task.
Ask: this endpoint fails for one caller and we cannot reproduce it locally. Find out why.
Strong looks like: they go looking for the panics rather than reading the whole handler. They know the difference between a panic that aborts a task and one that takes down the process, and which of the two this is on the runtime being used. The fix is a proper error path with a response the client can act on, not a catch-all that swallows the problem.
Weak looks like: replacing the unwrap with a default value so the panic stops. The request now succeeds with data nobody checked, which is worse than a 400.
Why it works: it is the most common Rust production bug by a distance, and it is a judgment question about what should fail and how loudly.
The setup. An async service that does something blocking in the middle of a handler: a synchronous file read, a CPU-heavy hash, a lock held across an await. Under concurrency the whole service becomes unresponsive while the machine looks almost idle.
Ask: latency goes to seconds under load and the CPU is at 20%. Explain.
Strong looks like: they connect executor starvation to the symptom and go looking for blocking calls rather than tuning anything. They can explain why the CPU graph looks the way it does, and they know the options: move the work to a blocking pool, make it asynchronous, or bound it. They also look at whether a lock is held across an await, which is the subtler version of the same bug.
Weak looks like: adding worker threads. It moves the wall slightly further away.
Why it works: async Rust is where most teams get hurt, and the failure mode is counter-intuitive enough that only people who have lived through it recognise it quickly.
The setup. A service where everything returns a single opaque error type, so a failure at the boundary says "internal error" whether the cause was bad input, a missing permission or a downstream timeout.
Ask: make these errors useful, for the caller and for us.
Strong looks like: they separate errors the caller can act on from errors they cannot, and they map that split onto the response. They talk about where context is added, and they keep the underlying cause for the logs without leaking it to the client. If they reach for a library, they can say what it is doing rather than just naming it.
Weak looks like: a bigger enum with twenty variants nobody matches on, or stringly-typed errors that are easy to produce and impossible to handle.
Why it works: error design is the part of Rust that shows whether somebody has maintained a service or only built one, and it is invisible in a toy exercise.
The setup. A function on the critical path that clones generously, allocates per iteration and builds intermediate collections. It is fine in a unit test and expensive under traffic.
Ask: make this faster, and show me the before and after.
Strong looks like: they profile or benchmark first, and they can say which change bought what. They know the difference between an allocation that matters and one that is noise, and they do not sacrifice clarity for a gain they have not measured. Nobody serious guesses here, because Rust performance intuition is frequently wrong.
Weak looks like: scattering references and lifetimes through the signature to avoid clones that cost nothing, making the code harder to read for no measured gain.
Why it works: teams choose Rust for performance and then write it like a garbage-collected language with stricter rules. This task finds out whether somebody can tell the difference, with evidence.
Four rows, the same for every candidate:
Nothing about the borrow checker. Fighting it is a first-week problem, and anybody who gets past the compiler has demonstrated the part interviews used to spend an hour on. The same framing for a different language is in the Go developer assessment.
These tasks need a service that builds, a load generator, a profiler and a client that sends the one malformed request. They also need a machine that can compile a Rust project without the candidate waiting ten minutes on a laptop with four cores.
EasyEnv gives the candidate a box with the project already built and the dependency cache warm, so the first compile is not the interview. The panic, the blocking call and the slow path are already in the code, identically for every candidate, so two people can be compared on the same problem. The box is disposable and the session is recorded, so you can see whether they profiled or guessed.
The limits, plainly. Preparing the project takes a day, and the bugs have to be realistic rather than obviously planted. Reviewing needs someone who has run async Rust in production, which is a small group. And for a role that is mostly CLI tooling rather than a service, task two is not the right hour.
Stop asking people to explain lifetimes. Give them a service that compiles cleanly and behaves badly: a panic on real input, a runtime that stalls at 20% CPU, errors that say nothing, and a hot path nobody has measured.
If you only run one, run the stalled runtime. Recognising executor starvation from a flat CPU graph is the clearest evidence that somebody has operated async Rust rather than read about it.
Run live coding sessions and take-home challenges in real production environments. Watch sessions back, score consistently, and hire with confidence.
More posts you might like
The backup job is green and nobody has ever run the other half. Here are four tasks on a real box that test whether a candidate can restore, verify, and tell you in minutes how much data was lost, which is the only number that matters during an incident.
Everyone says they know Git until a bad merge lands on main and the team loses a morning. Here are four tasks on a real repository, a conflict that is not textual, a commit that has to come out, a branch that has drifted, and a bug with an unknown origin.
Read moreMost API interviews turn into a style argument about plurals and status codes. The decisions that cost money are elsewhere. Here are four tasks on a running service that test retries, compatibility, pagination and error design, with what a strong answer sounds like.
Read more