Rust

  • By Language
  • 40 questions

What this round is for

Rust interviews are unusually focused. Ownership, borrowing and lifetimes account for most of the round, because they account for most of the language - everything else follows from the question of who owns this data and how long it lives.

The second theme is cost. Interviewers want to know whether you can say what the guarantee buys, what it costs, and when another language would be the better choice.

The questions

Answer each one out loud before you open it. An answer you have read is not an answer you can give under pressure, which is the only kind that counts.

Q1 What is ownership?

Show the answer Hide the answer

Every value has one owner, and the value is freed when the owner goes out of scope.

No garbage collector decides when, and no programmer has to remember. Single ownership is what makes a double free impossible by construction.

What it is really testing. Whether you connect it to the absence of a garbage collector and of double frees.

Q2 What is a move?

Show the answer Hide the answer

Assigning a non-Copy value transfers ownership, and the original binding becomes unusable.

Using it afterwards is a compile error rather than a run-time surprise, which is the whole point of tracking ownership statically.

What it is really testing. Whether you know the original binding becomes unusable, and why that is checked.

Q3 What is the Copy trait?

Show the answer Hide the answer

It marks types cheap enough to duplicate implicitly, so assignment copies rather than moves.

Integers, booleans and other simple types are Copy. For anything else you ask for a clone explicitly, so the cost is written down.

What it is really testing. Whether you know why clone must be explicit for everything else.

Q4 What are the borrowing rules?

Show the answer Hide the answer

Any number of immutable references, or exactly one mutable reference, never both at once.

That single rule prevents iterator invalidation, unexpected mutation and data races, all without run-time cost.

What it is really testing. Whether you can state the rule precisely. It is the foundation of everything else.

Q5 How do borrowing rules eliminate data races?

Show the answer Hide the answer

A race needs two accesses to the same data with at least one writing - which the rule forbids.

If concurrent Rust compiles, it has no data race. That is a narrow and genuine claim, not marketing.

What it is really testing. Whether you can explain the mechanism rather than repeating fearless concurrency.

Q6 What are lifetimes?

Show the answer Hide the answer

Annotations telling the compiler how long a reference is valid, so it can refuse a dangling one.

Most of the time they are inferred. They appear where the relationship between an input and an output reference is genuinely ambiguous.

What it is really testing. Whether you know they are mostly inferred rather than always written.

Q7 What is non-lexical lifetime?

Show the answer Hide the answer

A borrow ends at its last use rather than at the end of the block.

It made a large class of previously rejected but obviously correct programs compile, which removed much of the early frustration.

What it is really testing. Whether you know borrows end at last use, which modern Rust relies on.

Q8 How does Rust handle absence instead of null?

Show the answer Hide the answer

Option, with Some and None - so a missing value cannot be used by accident.

You must unwrap it, and unwrapping forces you to handle the empty case. The null reference is simply not expressible.

What it is really testing. Whether you connect it to the billion-dollar mistake being unexpressible.

Q9 How does Rust handle failure instead of exceptions?

Show the answer Hide the answer

Result, with Ok and Err, returned from any function that can fail.

The caller must deal with it, and the question-mark operator propagates an error upward in one character while keeping the handling visible.

What it is really testing. Whether you know the caller is forced to deal with it.

Q10 What does the question-mark operator do?

Show the answer Hide the answer

It returns early with the error if a Result is Err, otherwise unwraps the Ok value.

It keeps explicit error handling without the ceremony, which is why Rust code is less repetitive than Go despite the same philosophy.

What it is really testing. Whether you know it keeps handling visible while removing the ceremony.

Q11 When is unwrap acceptable?

Show the answer Hide the answer

In tests, prototypes, and where you can prove the value is present.

In production code it is a panic waiting for an unexpected input. expect with a message is better, because the panic then explains itself.

What it is really testing. Whether you prefer expect so the panic explains itself.

Q12 What is pattern matching and why is exhaustiveness valuable?

Show the answer Hide the answer

Destructuring a value and handling every case, with the compiler checking none is missed.

Add a variant a year later and every incomplete match fails to compile, handing you the list of places to update.

What it is really testing. Whether you value the compile error when a variant is added later.

Q13 What is a trait?

Show the answer Hide the answer

A set of methods a type can implement, similar to an interface.

Traits can have default methods, and generic code can accept any type implementing one, which is how abstraction works in Rust.

What it is really testing. Whether you know traits can carry default methods.

Q14 What is the difference between static and dynamic dispatch?

Show the answer Hide the answer

Generic code is specialised per type at compile time; dyn Trait looks the method up at run time.

Static dispatch has no run-time cost - that is what zero-cost abstraction means. Dynamic dispatch is opt-in and visible.

What it is really testing. Whether you know dynamic dispatch is opt-in and visible in the type.

Q15 What does zero-cost abstraction mean?

Show the answer Hide the answer

Expressive code compiles to the same machine code you would have written by hand.

An iterator chain typically produces the same assembly as the equivalent loop, with no intermediate collections allocated.

What it is really testing. Whether you can give the iterator example rather than repeating the phrase.

Q16 What is Box and when do you need it?

Show the answer Hide the answer

A pointer to heap data with a single owner, needed for recursive types and for dyn Trait.

A recursive enum has no known size at compile time, so the recursive field must be behind a pointer.

What it is really testing. Whether you know why a recursive type needs one.

Q17 What is Rc and when would you use it?

Show the answer Hide the answer

A reference-counted pointer allowing several owners of immutable data, freed when the last goes away.

It is single-threaded only. Arc is the atomic version for sharing across threads, at the cost of atomic operations.

What it is really testing. Whether you know Rc is single-threaded and Arc is the atomic version.

Q18 What is interior mutability?

Show the answer Hide the answer

Mutating through a shared reference, with the borrow rule enforced at run time instead of compile time.

RefCell panics if the rule is violated, which is a loud reproducible failure rather than silent corruption.

What it is really testing. Whether you know the check moves to run time rather than disappearing.

Q19 How do you share mutable state between threads?

Show the answer Hide the answer

Arc<Mutex<T>> - shared ownership with a lock guarding the data.

The lock returns a guard, and the data is only reachable through it, so forgetting to lock is not possible.

What it is really testing. Whether you know the guard makes forgetting to lock impossible.

Q20 What are Send and Sync?

Show the answer Hide the answer

Send means a type can move between threads; Sync means a reference can be shared between them.

They are automatic for most types and are what let the compiler reject unsound concurrent code without you writing anything.

What it is really testing. Whether you know they are automatic and what they let the compiler reject.

Q21 What is the difference between String and &str?

Show the answer Hide the answer

String owns its data and can grow; &str is a borrowed view of string data.

Taking &str as a parameter accepts both, which is why it is the idiomatic argument type.

What it is really testing. Whether you know &str is the idiomatic parameter type and why.

Q22 What is the difference between Vec and a slice?

Show the answer Hide the answer

Vec owns a growable buffer; a slice is a borrowed view of part of one.

Functions should usually take a slice so they work with arrays and vectors alike without forcing an allocation.

What it is really testing. Whether you take slices rather than forcing callers to allocate.

Q23 Why are iterators lazy and does it matter?

Show the answer Hide the answer

Nothing runs until something consumes the chain, so no step materialises an intermediate collection.

That is what lets a long pipeline over a huge sequence run without allocating, and why it compiles to the same code as a loop.

What it is really testing. Whether you connect laziness to the absence of intermediate allocations.

Q24 What is the difference between iter, into_iter and iter_mut?

Show the answer Hide the answer

iter borrows, iter_mut borrows mutably, into_iter takes ownership and consumes the collection.

Choosing into_iter when you still need the collection afterwards is a common early mistake.

What it is really testing. Whether you know into_iter consumes the collection.

Q25 What is the difference between a struct and an enum in Rust?

Show the answer Hide the answer

A struct holds all its fields; an enum is exactly one of several variants, each able to carry data.

Enums with data are far more powerful than enums in most languages, and they are how state is modelled correctly.

What it is really testing. Whether you know Rust enums carry data, unlike most languages.

Q26 How do you model a state machine in Rust?

Show the answer Hide the answer

An enum of states, each carrying only the data valid in that state, matched exhaustively.

Invalid combinations become unrepresentable rather than merely discouraged, which is a stronger guarantee than a convention.

What it is really testing. Whether you make invalid states unrepresentable rather than merely checked.

Q27 What is the Drop trait?

Show the answer Hide the answer

Code that runs when a value goes out of scope, used to release resources.

Because ownership is tracked, cleanup is deterministic - there is no collector deciding later, which C++ programmers will recognise as RAII.

What it is really testing. Whether you know cleanup is deterministic rather than collector-driven.

Q28 What does unsafe actually allow?

Show the answer Hide the answer

Dereferencing raw pointers, calling foreign functions, and a few other operations the compiler cannot verify.

It does not turn off the borrow checker. It narrows where the rules relax and makes those regions searchable and auditable.

What it is really testing. Whether you know it does not disable the borrow checker.

Q29 What is a macro in Rust and why are there two kinds?

Show the answer Hide the answer

Declarative macros match patterns; procedural macros run code to generate code at compile time.

Derive macros are the common case - one attribute generates an implementation that would be tedious and error-prone by hand.

What it is really testing. Whether you know derive macros are the common everyday case.

Q30 What is Cargo and what comes with it?

Show the answer Hide the answer

The build tool and package manager, with testing, benchmarking, documentation and publishing built in.

Doc comments are compiled and run as tests, so published examples cannot silently rot.

What it is really testing. Whether you know doc examples are compiled and tested.

Q31 How does async work in Rust?

Show the answer Hide the answer

Futures are lazy state machines that do nothing until polled by an executor.

The language provides the syntax and no runtime, so you choose an executor - which is unusual and occasionally surprising.

What it is really testing. Whether you know futures are lazy and the runtime is not built in.

Q32 What is the difference between async in Rust and in other languages?

Show the answer Hide the answer

Rust futures do nothing until awaited, and there is no built-in runtime.

Creating a future and dropping it performs no work at all, where in most languages the operation would already have started.

What it is really testing. Whether you know dropping a future performs no work at all.

Q33 What is a lifetime elision rule?

Show the answer Hide the answer

A convention letting the compiler infer lifetimes in common signatures so you do not annotate them.

It is why most functions need no annotations, and why the ones that do are genuinely ambiguous cases.

What it is really testing. Whether you know why most signatures need no annotation.

Q34 What is the orphan rule?

Show the answer Hide the answer

You may implement a trait for a type only if you own one of the two.

It prevents two crates defining conflicting implementations. The newtype pattern is the standard way around it.

What it is really testing. Whether you know what it prevents and how the newtype pattern works around it.

Q35 What is the newtype pattern?

Show the answer Hide the answer

Wrapping a type in a single-field struct to give it a distinct identity.

It enables trait implementations the orphan rule forbids, and it stops a user id and an order id being interchangeable.

What it is really testing. Whether you can name a use beyond trait implementations.

Q36 How do you handle errors in a library versus an application?

Show the answer Hide the answer

A library defines its own error type; an application can use a boxed or aggregated error.

Returning a concrete error type lets callers match on specific cases, which a boxed error prevents.

What it is really testing. Whether you know a boxed error prevents callers matching on cases.

Q37 What is the difference between panic and a Result error?

Show the answer Hide the answer

A panic is an unrecoverable bug; a Result is an anticipated failure the caller should handle.

Panics can be configured to abort or unwind, and a library that panics on bad input is making a decision for its caller.

What it is really testing. Whether you know a panicking library decides for its caller.

Q38 Why are compile times slow and what helps?

Show the answer Hide the answer

The compiler does a great deal of analysis and monomorphises generics for each concrete type.

Splitting into crates, reducing generic instantiation and using a faster linker all help; incremental builds help most.

What it is really testing. Whether you know monomorphisation is a major contributor.

Q39 What makes some data structures awkward in Rust?

Show the answer Hide the answer

Anything with shared mutable references or cycles - a doubly linked list, a graph.

They need reference counting, interior mutability or arena allocation. It is the honest cost of the ownership guarantee.

What it is really testing. Whether you can be honest about the cost rather than defending the language.

Q40 Where is Rust the right choice and where is it not?

Show the answer Hide the answer

Right where failure is expensive and performance matters; wrong for exploratory work that changes hourly.

Choosing it for a prototype means arguing with the compiler about the lifetimes of code you are going to delete.

What it is really testing. Whether you can name where it is the wrong tool.

Two things worth doing in the room

  • When asked about the borrow checker, describe a case where it refused something you believed was correct, and what you learned. Everyone has one, and it is a better answer than praise.
  • Say 'at compile time' explicitly. Much of Rust's value is moving checks from run time to compile time, and being precise about which is which shows you understand the trade.