Skip to main content

Interview questions

Rust engineer interview questions

Rust engineers build performance-sensitive systems where memory safety, concurrency and predictable resource usage matter. Interviews usually probe ownership, borrowing, async runtimes, unsafe boundaries and pragmatic tradeoffs against C, C++ or Go.

Seniorities
juniormidseniorstaffprincipal

Most reported

Top rust engineer interview questions

6 questions

Sorted by reported frequency. Click any question for the sample outline, follow-ups, and reference implementation.

As asked

You are designing a Rust library API that accepts user data, transforms it, and returns a result. When would you take ownership, borrow immutably, borrow mutably, or accept Cow?

Sample answer outline

A strong answer starts from the caller contract, not from syntax. Borrow immutably when the function only needs to read data and should not force allocation or transfer of lifetime responsibility. Borrow mutably when the function must update caller-owned state and exclusive access is part of the API contract. Take ownership when the callee must store, move across threads, or consume the value. Cow is useful when most calls can borrow but a minority need normalisation or copying. Candidates often trip by cloning to satisfy the compiler instead of redesigning ownership so the data flow is explicit.

Reference implementation (rust)

Rust
use std::borrow::Cow;

fn normalise_name(input: Cow<'_, str>) -> Cow<'_, str> {
    if input.chars().any(|c| c.is_uppercase()) {
        Cow::Owned(input.to_lowercase())
    } else {
        input
    }
}

Expect these follow-ups

  • How would your choice change if the value is stored in a background worker?
  • When is Clone an acceptable API requirement?
  • How do lifetimes appear in the public signature and what does that communicate?
ownershipapi-designlifetimes

As asked

A Go service slowly grows from 200 goroutines to 50,000 during normal traffic. Walk me through how you would diagnose and fix the leak.

Sample answer outline

Start with evidence: runtime metrics, pprof goroutine dumps and stack traces grouped by blocked call site. Common causes are sends to channels nobody receives from, receives on channels nobody closes, missing context cancellation and background tickers that are never stopped. The fix should remove the lifecycle ambiguity, usually by passing context.Context through the call chain, closing owned channels exactly once, and stopping tickers or timers. The candidate should also discuss regression protection with load tests or a goroutine-count assertion around the failing path. Interviewers look for ownership of goroutine lifetimes, not just knowing pprof commands.

Expect these follow-ups

  • How do you decide which side owns closing a channel?
  • What does a goroutine blocked on select usually tell you?
  • How would you expose goroutine growth as an alert?
goroutinespprofdebugging

As asked

A sensor produces data at irregular intervals and the device runs on battery. When would you use interrupts instead of polling, and what can go wrong?

Sample answer outline

Interrupts let the CPU sleep and react when the sensor signals data, which is often better for irregular events and power-sensitive devices. Polling can be simpler and more predictable when timing is fixed, the check is cheap, or the hardware interrupt line is unreliable. Keep interrupt service routines short: capture the event, clear the flag, and defer real work to the main loop or RTOS task. Discuss debouncing, missed interrupts, priority inversion, shared data races, and volatile or atomic access. Interviewers want practical embedded judgement, not a blanket rule that interrupts are always better.

Expect these follow-ups

  • How do you safely share data between an ISR and the main loop?
  • When can polling be more deterministic than interrupts?
  • How would you debug an interrupt storm in the field?
firmwareinterruptspower

As asked

Walk me through what happens to a value's ownership when you pass it to a function, and how that differs from languages with garbage collectors. Where exactly does the compiler insert the drop?

Sample answer outline

Strong answer covers the single-owner rule, that passing by value moves ownership into the callee's stack frame, and that the compiler inserts a drop at the end of the owning scope. Candidate should contrast this with GC languages where the runtime tracks reachability at runtime and reclaim asynchronously, noting Rust guarantees zero-cost deterministic cleanup.

Expect these follow-ups

  • What happens if you pass a type that implements Copy instead of moving it?
  • How does the compiler prove at compile time that a moved value is never used again?
ownershipmove-semanticsdropmemory-management

As asked

Describe the aliasing rules the Rust borrow checker enforces. What exactly makes the rule 'either one mutable reference or any number of shared references' safe, and how does the compiler verify it at compile time?

Sample answer outline

Strong answer references the XOR mutable aliasing rule and explains that it eliminates data races at compile time. Candidate should explain the borrow checker tracks borrows as non-overlapping regions, cite iterator invalidation as the classic mutable-alias hazard in C++, and note that the rule enables safe concurrent access patterns like multiple readers.

Expect these follow-ups

  • Can you have two mutable borrows to disjoint fields of the same struct?
  • How does split_at_mut work around the borrow checker?
borrow-checkeraliasingsafetyreferences

As asked

The compiler can often elide lifetimes. When does it fail to do so and require you to write explicit annotations? Give me a concrete function signature where elision would be ambiguous.

Sample answer outline

Candidate should state the three elision rules and explain that elision fails when there are multiple input lifetime parameters and the output lifetime cannot be tied to a single input, or when the compiler cannot determine whether the output should live as long as self or another parameter. A good concrete example is fn longest<'a>(x: &'a str, y: &'a str) -> &'a str showing why the return lifetime is ambiguous without annotation.

Expect these follow-ups

  • What does a lifetime annotation actually constrain at runtime?
  • How do lifetime bounds on structs interact with the struct's own lifetime?
lifetimesannotationselisiongenerics

Also known as

Rust developer - systems engineer - low-level software engineer