Rust Developer interview questions
100 real questions with model answers and explanations for Junior Rust Developer candidates.
See a Rust Developer resume example →Practice with flashcards
Spaced repetition · Hunter Pass
Questions
A variable binding cannot be assigned a new value unless it is declared with mut.
- let count = 1 creates an immutable binding, so count = 2 is rejected.
- let mut count = 1 allows later assignment while keeping the value's type unchanged.
- Immutability by default makes intended state changes explicit and easier to reason about.
Why interviewers ask this: The interviewer is checking whether you understand Rust's default binding behavior and the purpose of mut.
Shadowing creates a new binding with the same name, while mut permits changing the value of one existing binding.
- A new let can change the value's type because it introduces a distinct variable.
- A mutable assignment must keep the type established when the binding was declared.
- Shadowing is useful for staged transformations when the old value is no longer needed.
Why interviewers ask this: A strong answer distinguishes rebinding from mutation and knows why shadowing can change a type.
Rust's scalar types are integers, floating-point numbers, booleans, and characters.
- Integer types include signed i8 through i128 and isize, plus unsigned u8 through u128 and usize.
- Floating-point values use f32 or f64, and bool contains true or false.
- char is a four-byte Unicode scalar value, not a single UTF-8 byte.
Why interviewers ask this: The interviewer is evaluating basic type knowledge, including the important distinction between char and byte.
usize is the natural type for memory sizes and collection indices because its width matches the target platform's pointer width.
- Slice and array indexing expects usize.
- A fixed-width type such as u32 is preferable for a file format or protocol that requires exactly 32 bits.
- Conversions between integer types should be explicit because a value may not fit the destination type.
Why interviewers ask this: The question checks whether you connect integer choice to indexing, platform width, and external data contracts.
A tuple stores a fixed number of values that may have different types, while an array stores a fixed number of values of one type.
- Tuple elements are accessed by constant positions such as value.0 or through destructuring.
- An array type includes both element type and length, such as [u8; 16].
- Arrays are stack-allocated values and can be borrowed as slices for APIs that accept varying lengths.
Why interviewers ask this: The interviewer is checking your understanding of Rust's basic compound types and their type-level structure.
A statement performs an action without producing a usable value, while an expression evaluates to a value.
- Function calls, match blocks, if blocks, and ordinary blocks can all be expressions.
- The final expression of a block becomes that block's value when it has no trailing semicolon.
- Adding a semicolon usually turns an expression into a statement whose value is the unit type ().
Why interviewers ask this: A strong answer explains why Rust functions and blocks can return values without an explicit return keyword.
Ownership lets Rust manage memory and prevent invalid access without requiring a garbage collector.
- Every value has an owner that determines when the value is dropped.
- Assignments and function calls can transfer ownership, so the old binding may no longer be usable.
- Borrowing provides temporary access without transferring ownership.
Why interviewers ask this: The interviewer wants a clear mental model linking ownership to memory safety, cleanup, and borrowing.
Each value has one owner, there can be only one owner at a time, and the value is dropped when its owner leaves scope.
- Ownership can move from one binding to another for non-Copy values.
- A reference borrows a value and does not become its owner.
- The compiler enforces these rules before the program runs.
Why interviewers ask this: The interviewer is checking whether you can state the ownership model precisely rather than describe it only as memory safety.
Assigning a String normally moves its ownership to the new binding.
- String owns heap data, so copying only its stack metadata into two owners would risk a double free.
- After the move, the original binding cannot be used unless ownership is returned.
- Calling clone creates an independent String when both values are genuinely needed.
Why interviewers ask this: A strong answer connects move semantics to String's heap allocation and explains why the original binding is invalidated.
Passing an argument follows the same ownership rules as assignment: non-Copy values move, while Copy values are copied.
- A function that takes String by value owns it and may drop it before returning.
- Taking &str or &String borrows the data instead of consuming the caller's value.
- Returning the value can transfer ownership back, but borrowing is usually clearer when consumption is unnecessary.
Why interviewers ask this: The interviewer is evaluating whether you can predict ownership across a basic function boundary.
Copy means assignment and argument passing duplicate a value implicitly instead of moving it.
- Simple scalar types and tuples containing only Copy values commonly implement Copy.
- A type that implements Drop cannot also implement Copy because implicit duplication would conflict with unique cleanup.
- Copy is intended for cheap bitwise duplication and must be explicitly implemented or derived for user-defined types.
Why interviewers ask this: The interviewer is checking whether you understand the semantic effect and important restrictions of Copy.
Clone performs an explicit duplication through the clone method, while Copy allows implicit duplication on assignment and argument passing.
- Clone may allocate memory or run custom logic, as it does when duplicating a String's buffer.
- Every Copy type must also implement Clone, but many Clone types are not Copy.
- Calling clone should express a real need for another owned value rather than silence an ownership error automatically.
Why interviewers ask this: A strong answer distinguishes explicit potentially expensive duplication from implicit cheap copying.
A value is dropped when its owner leaves scope, unless ownership was moved elsewhere first.
- Rust calls the Drop implementation and then releases the value's owned resources.
- Local variables are generally dropped in reverse order of their creation.
- The std::mem::drop function can end ownership explicitly before the surrounding scope ends.
Why interviewers ask this: The interviewer is checking your basic understanding of deterministic resource cleanup and moved values.
Borrowing gives temporary access to a value through a reference without transferring ownership.
- A shared reference has type &T and permits reading the borrowed value.
- A mutable reference has type &mut T and permits changing the borrowed value.
- The owner remains responsible for dropping the value after all borrows end.
Why interviewers ask this: The interviewer wants to see a direct distinction between temporary access and ownership transfer.
At a given time, Rust permits either any number of shared references or one mutable reference to the same value.
- Shared references allow concurrent reading but not mutation through those references.
- An exclusive mutable reference prevents other references from observing a value during mutation.
- These rules prevent data races and invalidated references at compile time.
Why interviewers ask this: A strong answer states the exclusivity rule and connects it to the safety property it provides.
Mutation through &mut T is permitted because the reference grants exclusive mutable access to its target.
- The original value must be declared mut before code can take a mutable reference to it.
- Rebinding a variable that stores a mutable reference is separate from mutating the referenced value.
- Function parameters written as value: &mut T can mutate T without taking ownership of it.
Why interviewers ask this: The interviewer is checking whether you distinguish a mutable target, a mutable reference, and a mutable binding.
Rust rejects a reference that could outlive the value it points to because using it would access invalid memory.
- A local value is dropped when its owner leaves scope.
- Returning a reference to that local value would leave the caller pointing to released storage.
- Returning an owned value transfers valid ownership to the caller and is the usual solution.
Why interviewers ask this: The interviewer is evaluating whether you understand the memory-safety reason behind lifetime checking.
A lifetime is the region of code for which a reference is valid.
- The compiler compares lifetimes to ensure a borrowed value outlives every use of its reference.
- Most lifetimes are inferred and require no written annotation.
- A lifetime annotation describes relationships between references but does not make any value live longer.
Why interviewers ask this: A strong answer defines lifetimes as validity relationships rather than runtime timers or memory extension.
An annotation is needed when the compiler must know which input borrow the returned reference is tied to.
- A signature like fn longest<'a>(x: &'a str, y: &'a str) -> &'a str states that the result is valid for their shared usable lifetime.
- The annotation does not require both arguments to have identical concrete lifetimes.
- It prevents the returned reference from being used longer than either possible source allows.
Why interviewers ask this: The interviewer is checking whether you can read a basic lifetime relationship in a function signature.
The lifetime parameter records that an instance of the struct cannot outlive the data referenced by one of its fields.
- A field such as text: &'a str ties the field's validity to 'a.
- The compiler then prevents the struct from remaining in use after text's source is dropped.
- Owning a String instead removes that borrowing relationship but changes allocation and ownership semantics.
Why interviewers ask this: The question tests whether you understand how borrowed fields constrain the lifetime of their containing struct.
Locked questions
- 21
What does the 'static lifetime mean?
lifetimes - 22
How do you define and construct a struct in Rust?
types - 23
What ownership effect can struct update syntax have?
ownershiptypes - 24
What is the difference between an associated function and a method in an impl block?
types - 25
What are tuple structs and unit-like structs?
types - 26
How is a Rust enum more powerful than a simple numeric enumeration?
enums - 27
Why must match expressions be exhaustive?
control-flowbasics - 28
How do patterns destructure values in a match arm?
destructuringcontrol-flow - 29
When is if let preferable to match?
control-flow - 30
What do _ and .. mean in Rust patterns?
- 31
What does Option<T> represent?
error-handling - 32
What are map, and_then, and unwrap_or used for on Option<T>?
error-handling - 33
What does Result<T, E> represent?
error-handling - 34
How do map and map_err transform a Result?
error-handling - 35
What does the ? operator do with a Result?
error-handling - 36
How does the ? operator behave with Option?
error-handling - 37
When should a Rust function return Result instead of panicking?
error-handlingerrors - 38
What is the difference between unwrap and expect?
error-handling - 39
What is a trait in Rust?
traits - 40
How do you implement a trait for a type?
traits - 41
What is a trait bound on a generic type parameter?
genericstraits - 42
What does #[derive(...)] do for traits?
traits - 43
What is Vec<T>, and how does it differ from an array?
- 44
What is a slice in Rust?
types - 45
How do String and &str differ?
- 46
Why does Rust not allow indexing a String with an integer?
indexes - 47
What are Cargo and Cargo.toml used for?
cargo - 48
What is the difference between a package, a crate, and a module in Rust?
cargo - 49
What do pub and use do in Rust's module system?
system-design - 50
What roles do Cargo.toml and Cargo.lock play in dependency management?
dependenciescargo - 51
A function passes a String to a logging helper and then needs to use it again; how would you avoid an unnecessary move?
logging - 52
How would you design a function that only reads a caller's String?
design - 53
How would you write a function that appends text to a caller-owned String?
- 54
The borrow checker rejects two simultaneous mutable references to one Vec; how would you fix the code?
ownership - 55
A Vec is immutably borrowed for its first element and then mutated with push; how would you resolve the error?
immutabilityownership - 56
A function tries to return &str pointing into a local String; how would you fix it?
- 57
Code moves a String field out of a struct and then tries to use the whole struct; how would you avoid the partial move?
types - 58
A loop passes each owned String into a helper and then needs the same value afterward; how would you change the helper or loop?
- 59
How would you read an optional command-line argument and use a default when it is absent?
error-handling - 60
How would you convert an Option<String> into an Option<usize> containing the string length?
error-handlingtypes - 61
When a configuration value is Option<u16>, how would you obtain a port with 8080 as the fallback?
configerror-handling - 62
How would you parse an Option<String> as an integer while keeping invalid or missing input as None?
error-handling - 63
How would you use ? in a function that returns Option<T>?
error-handling - 64
How would you parse a string as i32 and return the parse error to the caller?
- 65
How would you use ? while reading a file and parsing its contents in a Result-returning function?
error-handling - 66
A function uses ? on two operations with different error types; how would you make the errors compatible?
- 67
How would you handle NotFound differently from other file-opening errors?
- 68
Production code calls unwrap on user input; how would you improve it?
error-handling - 69
How would you collect only the even numbers from a Vec<i32> into a new Vec?
- 70
How would you create a Vec<usize> containing the lengths of borrowed strings without consuming them?
ownershiptypes - 71
How would you count word frequencies with a HashMap?
- 72
How would you insert a computed value into a HashMap only when the key is missing?
- 73
How would you read an element at a user-provided Vec index without risking a panic?
indexeserrors - 74
How would you sort a Vec of records by an integer field while keeping the records?
- 75
How would you remove duplicate adjacent values from a Vec, and what if duplicates are scattered?
- 76
How would you sum only the positive values from an iterator of i32 values?
iterationiterators - 77
How would you find the first user with a matching ID in a Vec<User>?
control-flow - 78
How would you print each collection item with its zero-based position?
- 79
How would you check whether any value is invalid and whether all values are valid?
- 80
How would you combine names and scores from two vectors into pairs?
- 81
How would you process a slice in fixed groups of three without manual index arithmetic?
concurrencytypesindexes - 82
How would you parse a comma-separated line into trimmed, non-empty fields?
- 83
How would you join a Vec<String> into one comma-separated string?
joins - 84
How would you safely take the first Unicode scalar value from a UTF-8 String?
types - 85
How would you execute different behavior for Create, Delete, and Quit variants of a command enum?
enums - 86
How would you run code only when an Option contains a value and ignore None?
error-handling - 87
How would you repeatedly pop values from a Vec until it is empty?
hypothesis-testing - 88
How would you destructure a Point { x, y } while accepting only points with a positive y value?
destructuring - 89
How would you match an HTTP-like status code into success, client error, server error, and other ranges?
httpstatus-codescontrol-flow - 90
How would you expose distinct NotFound and InvalidInput failures from a small library function?
distinct - 91
How would you add the file path to an error in a command-line application using anyhow?
error-handling - 92
How would you deserialize a JSON string into a Rust configuration struct with serde?
configtypes - 93
A JSON field may be missing; how would you represent that with serde?
- 94
How would you write a unit test for a function that adds two numbers?
unit - 95
How would you test a function that returns Result without calling unwrap blindly?
error-handling - 96
Before submitting a Rust change, which Cargo checks would you run and how would you respond to failures?
cargo - 97
How would you call an async function from a small Tokio command-line program?
async-runtimeasync - 98
tokio::spawn rejects a task that borrows a local String; how would you fix the ownership error?
ownershipasync-runtime - 99
How would you run two independent async operations concurrently and wait for both results?
asyncconcurrencyerror-handling - 100
How would you prevent an async network operation from waiting forever in Tokio?
async-runtimeasync