25 Rust interview questions
Structure the Rust interview loop around evidence, not language trivia. Start with a 30-minute screen covering ownership, async experience, production debugging, and recent Rust work. Use a 60–90 minute coding interview to observe API design, error handling, tests, and comfort with the borrow checker under pressure. Add a practical systems/debugging round that uses current stable Rust with the 2024 edition, Cargo, clippy, rustfmt, tracing, and cargo-nextest. For mid and senior roles, include a design round focused on concurrency, data ownership, failure modes, and operational behavior. Close with a collaboration interview: code review style, trade-off discussion, and how the candidate explains unsafe code, dependencies, and maintenance risk.
Junior Rust questions
Fundamentals, learning speed and how they debug.
01You receive a borrow-checker error while trying to update a value in a collection and also read from the same collection. How would you approach fixing it without just cloning everything?
Listen for: A strong answer separates the read and write phases, narrows lifetimes with smaller scopes, uses indexes or keys carefully, or reaches for methods such as entry, split_at_mut, or drain when appropriate. They should explain why cloning may be acceptable for small values but should not be the default escape hatch.
Red flag: They treat the borrow checker as something to fight with unsafe or widespread clone calls rather than a design signal.
02Walk through how you would model an operation that can fail in several expected ways and one unexpected way in a small Rust service.
Listen for: Look for Result-based control flow, specific error variants with thiserror, preserving source errors, and converting to user-facing responses at the boundary. They should distinguish recoverable domain errors from bugs or infrastructure failures, avoid stringly typed errors, and know when anyhow is acceptable for application code.
Red flag: They use panic, unwrap, or String errors for normal failure paths.
03A test sometimes fails on CI but not locally. What would you inspect in a Rust project first?
Listen for: They should mention running tests with cargo test and cargo-nextest, checking shared state, temp files, current working directory, time assumptions, environment variables, test parallelism, and feature flags. Good candidates add deterministic seeds, better assertions, and tracing logs rather than simply retrying the test.
Red flag: They immediately blame CI without isolating nondeterminism or shared state.
04How do you decide whether to pass a value by ownership, shared reference, mutable reference, or something like Arc?
Listen for: A good answer ties the choice to who owns the data, whether mutation is needed, how long the value must live, and whether it crosses thread or task boundaries. They should understand that Arc shares ownership, not mutability, and that references are simpler when lifetimes remain local.
Red flag: They use Arc<Mutex<T>> as a general solution for ownership problems.
05You are reviewing a pull request that adds several unwrap calls. How would you respond?
Listen for: Listen for judgment instead of a blanket rule. unwrap can be acceptable in tests, examples, impossible states with clear comments, or startup validation where crashing is preferred. In request handling or background jobs, they should prefer propagated errors, context, and observability so failures are diagnosable.
Red flag: They say unwrap is always fine because Rust will crash safely.
06What would you include in a small Rust CLI project to make it maintainable for another engineer?
Listen for: They should discuss clear module boundaries, clap for argument parsing, serde for config when needed, thiserror or anyhow for errors, integration tests using assert_cmd or temp dirs, rustfmt, clippy, README examples, and predictable exit codes. Strong answers keep the design simple and avoid premature async or abstraction.
Red flag: They focus only on making the code compile and ignore user behavior, tests, and errors.
Mid-level Rust questions
Independent delivery, trade-offs and code quality.
07You need to add async database access to an existing synchronous Rust service. How would you plan the change?
Listen for: A strong answer identifies runtime boundaries, blocking calls, connection pooling, cancellation behavior, and incremental migration. They may choose Tokio with sqlx, keep sync code behind spawn_blocking temporarily, or avoid async if throughput does not require it. They should discuss tests, tracing spans, and rollout risk.
Red flag: They propose making every function async without considering blocking code or API churn.
08Describe a time or approach for reducing excessive cloning in Rust code without making it unreadable.
Listen for: Look for profiling or allocation evidence first, then targeted changes: borrowing at API boundaries, Cow for conditional ownership, Arc for shared immutable data, interning in specific cases, or restructuring loops. They should avoid lifetime-heavy public APIs unless the performance benefit is real and maintainable.
Red flag: They optimize by adding complex lifetimes everywhere before measuring allocations or latency.
09How would you design error handling in a library crate versus an application binary?
Listen for: They should separate stable, typed library errors from flexible application context. A library should expose meaningful error enums, preserve sources, avoid anyhow in public APIs, and not print or exit. A binary can use anyhow for top-level context, map errors to responses, and centralize logging.
Red flag: They use the same anyhow-based error type in public library APIs and command-line entry points.
10A Tokio service shows high latency under load. What Rust-specific causes would you investigate?
Listen for: Strong candidates check blocking work on async workers, mutex contention across await points, unbounded channels, connection pool saturation, oversized tasks, missing backpressure, and excessive allocation. They should mention tokio-console, tracing, flamegraphs, metrics, and replacing std::sync::Mutex with appropriate async or parking_lot choices when justified.
Red flag: They assume async automatically prevents blocking and do not inspect runtime behavior.
11How do you evaluate adding a new crate to a production Rust codebase?
Listen for: They should consider maintenance activity, MSRV, unsafe usage, transitive dependencies, feature flags, license, security advisories through cargo-audit or cargo-deny, compile time, API stability, and whether the crate solves a real problem. Good answers include wrapping dependencies at boundaries to reduce future migration cost.
Red flag: They choose crates only by download count or the first search result.
12You find lifetime parameters spreading through many structs after a refactor. What options do you consider?
Listen for: Listen for practical design thinking: own data at longer-lived boundaries, borrow only inside short operations, use Arc<str>, String, Bytes, or Cow where appropriate, or split parsing from storage. They should recognize when lifetimes express useful constraints and when they make APIs brittle.
Red flag: They either remove all lifetimes by cloning blindly or insist every API must borrow.
13How would you make a Rust HTTP endpoint easier to diagnose in production?
Listen for: They should mention structured tracing with request IDs, span fields rather than ad hoc logs, error classification, latency metrics, dependency timing, meaningful status mapping, and avoiding sensitive data in logs. Current tools may include tracing, tower-http, OpenTelemetry integration, axum 0.8, and cargo-nextest for regression tests.
Red flag: They add println! statements or log full request bodies without considering privacy or volume.
Senior Rust questions
Judgement, architecture and raising the team's bar.
14A team wants to introduce unsafe code for performance in a hot path. How would you review and govern that change?
Listen for: A senior answer starts with measurement and alternatives, then requires a narrow unsafe boundary, documented invariants, safe public API, tests, fuzzing where relevant, Miri, sanitizers when applicable, and code ownership. They should discuss review standards, future maintainability, and how to prove the performance win remains valid.
Red flag: They accept unsafe because it is faster in theory or reject it categorically without analysis.
15How would you structure a Rust workspace used by several services and shared libraries?
Listen for: Look for workspace-level dependency management, feature discipline, clear crate boundaries, avoiding cyclic domain coupling, shared observability and error patterns, CI that runs clippy, rustfmt, tests, cargo-deny, and nextest. They should avoid a giant common crate and explain how ownership and release coordination work.
Red flag: They put all shared code into one utils crate with broad features enabled by default.
16A public Rust API you maintain has a design mistake. How do you evolve it without breaking users unnecessarily?
Listen for: They should discuss semver, deprecation warnings, additive APIs, feature gates only when useful, migration docs, type aliases or adapter traits, and reserving breaking changes for major versions. Strong candidates understand Rust API stability issues around trait methods, enum variants, generics, and exposed dependency types.
Red flag: They say to just change the API because the compiler will tell users what to fix.
17How do you decide between threads, Tokio tasks, channels, locks, and actors for a concurrent Rust component?
Listen for: A strong answer weighs workload type, shared state, cancellation, ordering, backpressure, failure isolation, and observability. They should distinguish OS threads from async tasks, prefer message passing when ownership transfer is natural, use locks for simple protected state, and avoid holding guards across await points.
Red flag: They apply one concurrency model everywhere regardless of workload or failure behavior.
18Your Rust service has occasional memory growth but no obvious leak. How would you investigate?
Listen for: Listen for differentiating leaks from caches, queues, fragmentation, and retained Arcs. They should inspect heap profiles, channel depths, task lifetimes, connection pools, metrics, and drop behavior. Good answers mention tools such as heaptrack, dhat, jemalloc profiling, tokio-console, and targeted load tests.
Red flag: They assume Rust cannot have memory leaks because it has ownership.
19How would you guide a team new to Rust without slowing delivery too much?
Listen for: Strong answers combine coding standards, small examples, pairing, focused reviews, and reusable patterns for errors, async, config, tests, and observability. They avoid abstract lifetime lectures until needed, choose approachable crates, keep unsafe out of early work, and create CI feedback with clippy and rustfmt.
Red flag: They expect everyone to learn by reading the Rust Book alone and failing reviews repeatedly.
20A dependency upgrade changes trait bounds and breaks many crates in the workspace. How do you handle it?
Listen for: They should isolate the breakage, read release notes, use cargo tree, cargo update -p, feature unification checks, and staged PRs. Good candidates consider compatibility shims, pinning temporarily, coordinating crate owners, updating MSRV if needed, and adding tests to capture changed behavior.
Red flag: They force the upgrade in one large PR without understanding feature or trait-bound changes.
System design Rust questions
Designing real systems end to end.
21Design a Rust service that ingests events from Kafka or NATS, validates them, writes to Postgres, and exposes processing health over HTTP. What are the key ownership and failure-boundary decisions?
Listen for: Strong answers separate ingestion, validation, persistence, and health crates or modules; use typed messages; define retry and dead-letter behavior; apply backpressure with bounded channels; and avoid sharing mutable state casually. They should discuss Tokio cancellation, sqlx pooling, tracing spans, idempotency, and graceful shutdown.
Red flag: They design a single async loop with unbounded queues and no idempotency or shutdown plan.
22Design a Rust library crate for parsing a large binary file format that downstream teams will use in services and CLIs.
Listen for: Look for streaming versus full-load trade-offs, zero-copy only where it does not poison ergonomics, clear error types, fuzz tests, corpus tests, no unexpected panics, and a stable public API. They should discuss optional serde support, feature flags, MSRV, documentation examples, and unsafe avoidance or containment.
Red flag: They expose internal lifetimes and dependency types everywhere before validating user needs.
23Design a low-latency Rust API gateway that calls several downstream services and enforces timeouts, retries, and rate limits.
Listen for: They should cover Tower middleware, reqwest or hyper choices, per-hop deadlines, cancellation propagation, bounded concurrency, circuit breaking, structured tracing, and response classification. Strong answers avoid retrying non-idempotent calls blindly and consider config reloads, connection reuse, TLS, and protecting the Tokio runtime from blocking work.
Red flag: They add retries around every failure without budgets, idempotency rules, or backpressure.
24Design a background job system in Rust for scheduled and ad hoc jobs, with visibility into failures and safe deploys.
Listen for: A good design covers durable job state, locking or leasing, idempotent handlers, cancellation, retries with jitter, dead-letter handling, and schema evolution. Rust-specific signals include trait-based job handlers with object-safety awareness, typed payloads, tracing, graceful shutdown, and avoiding long-held locks across await points.
Red flag: They rely on in-memory queues for durable work without explaining failure recovery.
25Design a plugin or extension system for a Rust application where third parties add behavior safely.
Listen for: Listen for choosing among trait-based in-process plugins, dynamic libraries, WASM runtimes, or external processes based on trust and isolation. They should discuss ABI instability for Rust dylibs, versioned interfaces, serde-based boundaries, resource limits, capability control, testing contracts, and how to keep unsafe code contained.
Red flag: They assume Rust trait objects can be loaded across arbitrary dynamic library boundaries without compatibility concerns.
Build and instrument a small async Rust work queue
Implement a Rust 2024 edition service that accepts JSON jobs over HTTP, stores them in memory, processes them with bounded concurrency, and exposes job status. Use Tokio and an HTTP framework such as axum 0.8. Jobs can be simple string transformations with simulated delay and deterministic failures. Include tests for success, failure, cancellation or shutdown behavior, and queue limits. Add structured tracing and clear error responses. The exercise should take 2–3 hours; persistence, authentication, and a UI are intentionally out of scope.
How to evaluate it
- Clear ownership model and bounded concurrency rather than global mutable state or unbounded spawning.
- Idiomatic error handling with meaningful HTTP responses and no unwraps on request paths.
- Tests cover normal flow, failures, and at least one concurrency or shutdown edge case.
- Readable API and module structure that would be easy to extend to durable storage later.
- Use of current Rust tooling: cargo fmt, clippy-clean code, tracing, and documented run/test commands.
Rather skip the interview loop?
Every Octogle Rust engineer has already passed a technical assessment, system design and a live interview with an engineer who has shipped Rust. Send a brief and get 3–5 scored profiles in 48 hours.