Perguntas de entrevista: Rust Developer
100 perguntas reais com respostas modelo e explicações para candidatos Pleno.
Ver um exemplo de currículo: Rust Developer →Treinar com flashcards
Repetição espaçada · Hunter Pass
Perguntas
A lifetime parameter relates the validity of borrowed inputs and outputs without extending any value's lifetime.
- It tells the borrow checker which references must remain valid for at least the same region.
- A return lifetime normally ties the returned reference to one or more input borrows.
- Lifetimes describe relationships already present in the program and do not allocate, clone, or keep data alive.
Por que perguntam isso: The interviewer checks whether you treat lifetimes as relationships between borrows rather than runtime duration controls.
Lifetime elision applies a small set of deterministic rules to common reference signatures.
- Each elided input reference receives its own lifetime parameter.
- If there is exactly one input lifetime, it is assigned to every elided output reference.
- For methods, the lifetime of &self or &mut self is assigned to elided output references, otherwise an ambiguous output requires annotation.
Por que perguntam isso: A strong answer names the rules and knows when explicit annotations become necessary.
A &'static T is a reference valid for the entire program, while T: 'static means T contains no non-static borrowed data.
- String literals are common &'static str values because their bytes live in the program binary.
- An owned String satisfies 'static even though the String value itself can be dropped at any time.
- A type containing a short-lived reference does not satisfy T: 'static unless that reference is itself static.
Por que perguntam isso: The interviewer is testing a frequent source of confusion in thread, task, and trait bounds.
The bound T: 'a requires every reference contained inside T to remain valid for at least lifetime 'a.
- It does not require the value of T to be borrowed for all of 'a.
- The bound matters when T is stored behind a reference or inside another type that lives for 'a.
- Modern Rust often infers outlives bounds, but explicit bounds are still needed when the relationship is not implied.
Por que perguntam isso: A strong answer distinguishes an outlives constraint on contained references from ownership of the value itself.
A higher-ranked trait bound requires an implementation to work for any lifetime chosen by the caller.
- for<'a> Fn(&'a str) accepts a closure that can borrow a str for every possible input lifetime.
- A bound using one outer lifetime would support only that particular lifetime rather than all calls.
- This pattern appears in callbacks, parsers, and APIs that repeatedly lend temporary references.
Por que perguntam isso: The interviewer checks whether you can express lifetime-generic behavior rather than one fixed borrow region.
Tie the output to the specific input from which the returned reference can originate.
- If the function may return either input, both usually need one shared lifetime representing their overlap.
- If only one input can supply the result, give the other borrow a separate lifetime so the signature is not overly restrictive.
- The implementation must satisfy the declared relationship on every control-flow path.
Por que perguntam isso: A strong answer uses the least restrictive honest lifetime relationship instead of assigning one lifetime to every reference.
Variance determines whether a value with a longer lifetime can be used where a shorter one is required.
- Shared references &'a T are covariant in 'a, so a longer valid borrow can be shortened safely.
- Mutable references are covariant in their lifetime but invariant in T because mutation could store an incompatible value.
- Interior-mutability types such as Cell<T> are invariant in T for the same write-safety reason.
Por que perguntam isso: The interviewer is evaluating deeper understanding of why some apparently safe lifetime coercions are rejected.
Moving a struct can invalidate an internal reference that points to another field of the same struct.
- A normal value may change address through assignment, return, or collection reallocation.
- Lifetimes alone cannot promise that the owner will remain at one stable address.
- Prefer indices or owned data, and use pinning only when an API genuinely requires address stability.
Por que perguntam isso: A strong answer connects the problem to moves and understands why lifetime annotations alone cannot solve it.
Use inherent methods for operations intrinsic to one type and traits for shared capabilities or extension points.
- Inherent methods need no trait import and cannot be implemented by unrelated types.
- Trait methods support generic bounds, multiple implementations, and behavior shared across a family of types.
- Public traits should represent a coherent contract rather than serve only as namespaces for helper functions.
Por que perguntam isso: The interviewer checks whether API shape follows extensibility needs instead of placing every method in a trait.
A where clause expresses the same bounds as inline syntax but scales better for complex relationships.
- It keeps the type parameter list readable when several parameters have multiple bounds.
- It can constrain associated types, references, and relationships that are awkward to place after a parameter name.
- Bounds should state only capabilities the implementation actually uses so callers retain maximum flexibility.
Por que perguntam isso: A strong answer combines readable syntax with disciplined, minimal generic constraints.
Use an associated type when each implementation should choose one canonical related type.
- Iterator uses an associated Item because one iterator implementation yields one item type.
- A generic trait parameter permits the same concrete type to implement the trait several times for different parameter choices.
- Associated types reduce repeated annotations and let bounds refer to relationships such as Iterator<Item = String>.
Por que perguntam isso: The interviewer is testing whether you can model one-per-implementation and many-per-implementation relationships correctly.
A trait object erases the concrete type and stores access to data plus a virtual method table.
- dyn Trait is dynamically sized and therefore appears behind a pointer such as &dyn Trait or Box<dyn Trait>.
- The data pointer locates the concrete value, while the vtable provides method, size, alignment, and drop information.
- Trait objects allow heterogeneous concrete types to share one runtime-polymorphic interface.
Por que perguntam isso: A strong answer explains both type erasure and the fat-pointer representation behind dynamic dispatch.
Static dispatch specializes generic code for concrete types, while dynamic dispatch selects methods through a trait object's vtable.
- Generics enable inlining and type-specific optimization but can increase compile time and binary size through monomorphization.
- Trait objects reduce code duplication and support heterogeneous collections at the cost of indirection and fewer optimization opportunities.
- Choose based on API flexibility, performance evidence, and whether concrete types must coexist at runtime.
Por que perguntam isso: The interviewer expects a trade-off analysis rather than the claim that one dispatch form is always faster or better.
A dyn-compatible trait must allow callers to invoke its object-safe methods without knowing the concrete Self type.
- Methods used through the object cannot return Self or use unconstrained generic type parameters.
- Methods requiring Self: Sized may remain on the trait but are unavailable through a trait object.
- Associated types must be specified when constructing the object if the trait exposes them.
Por que perguntam isso: A strong answer links dyn compatibility rules to the information available in a vtable call.
A supertrait bound makes the parent capability a requirement for every implementation and use of the child trait.
- Implementing Parser then requires the concrete type to satisfy Send and Sync.
- Generic code bounded by Parser may rely on those thread-safety properties without repeating them.
- Add supertraits only when they are fundamental to the abstraction because they restrict all implementations.
Por que perguntam isso: The interviewer checks whether inherited bounds are used as real semantic requirements rather than convenience.
Rust permits an implementation only when the current crate owns either the trait or the target type.
- The rule prevents two dependency crates from providing conflicting implementations for the same trait and type.
- A crate cannot directly implement a foreign trait for a foreign type.
- The newtype pattern creates a local wrapper that can receive the desired implementation without changing the underlying representation.
Por que perguntam isso: A strong answer connects the orphan rule to globally unambiguous method and trait resolution.
A blanket implementation provides a trait for every type satisfying stated bounds.
- An implementation such as impl<T: Display> Loggable for T removes repetitive per-type code.
- It becomes part of coherence and may block a more specific implementation that overlaps later.
- Public libraries should use blanket implementations carefully because downstream crates cannot override them.
Por que perguntam isso: The interviewer is evaluating reuse together with awareness of long-term compatibility constraints.
Default methods should derive useful behavior from the trait's required core operations.
- Implementors gain consistent behavior without duplicating code and may override it when the contract permits.
- A default should not assume hidden invariants that required methods do not express.
- Adding a method with a default is often compatible, while adding a required method breaks every existing implementation.
Por que perguntam isso: A strong answer considers both abstraction quality and trait evolution.
Use fully qualified syntax when several traits or an inherent implementation provide the same method name.
- The form <Type as Trait>::method makes the selected implementation explicit.
- It also allows calling associated functions that have no receiver from which Rust could infer the trait implementation.
- Ordinary method syntax is preferable when resolution is unambiguous because it keeps call sites concise.
Por que perguntam isso: The interviewer checks whether you can resolve method ambiguity without renaming coherent trait APIs.
The compiler infers the least demanding capture mode required by the closure body.
- Reading an outer value usually captures it by shared reference.
- Mutating an outer value requires a mutable borrow, while consuming it requires capture by value.
- The capture mode helps determine which of Fn, FnMut, and FnOnce the closure implements.
Por que perguntam isso: A strong answer connects capture analysis to both ownership and closure trait selection.
Perguntas bloqueadas
- 21
How do Fn, FnMut, and FnOnce differ?
- 22
What does the move keyword change for a Rust closure?
closures - 23
How can a function return or store a closure when every closure has a unique type?
closures - 24
Why does thread::spawn commonly require a move closure with 'static captures?
closuresconcurrency - 25
Why are Rust iterator adapters lazy?
iterationiterators - 26
How does the Iterator trait represent iteration state and yielded values?
iterationtraitsiterators - 27
When would you choose map, filter_map, or flat_map in an iterator pipeline?
arraysiterationiterators - 28
Why does collect sometimes need a type annotation?
- 29
How do iter, iter_mut, and into_iter differ for a collection?
- 30
How would you implement a custom iterator in Rust?
iterationiterators - 31
What role does IntoIterator play in for loops and generic APIs?
generics - 32
How can an iterator pipeline stop on the first Result error?
iterationerror-handlingiterators - 33
When is Box<T> the appropriate smart pointer?
smart-pointers - 34
How do Rc<T> and Weak<T> work together?
- 35
How does Arc<T> differ from Rc<T>?
- 36
What guarantees and risks does RefCell<T> introduce?
smart-pointers - 37
When should Cell<T> be preferred over RefCell<T>?
smart-pointers - 38
What is the interior mutability pattern and when is it justified?
- 39
How do Deref and deref coercion affect smart-pointer APIs?
fundamentals - 40
How should a reusable Rust library define custom error types?
- 41
When should a Rust project use thiserror versus anyhow?
error-handling - 42
How does the ? operator propagate and convert errors?
- 43
How should error context be added without losing the original cause?
- 44
What does JoinHandle represent for a spawned Rust thread?
concurrency - 45
What do the Send and Sync marker traits guarantee?
traitsconcurrency - 46
How does Arc<Mutex<T>> provide shared mutable state across threads?
concurrencyownership - 47
How do Rust channels transfer data between threads?
concurrency - 48
When should concurrent Rust code prefer message passing over shared state?
concurrency - 49
What does an async fn return, and how is its Future executed?
async - 50
What is the difference between sequential awaiting and concurrent async composition?
asyncconcurrencyoop - 51
A function takes `&mut Vec<Record>`, stores `let record = &mut records[i]`, pushes a new record, and then updates `record`, causing a second mutable-borrow error. How would you fix the code without cloning the whole record?
ownership - 52
You iterate over `jobs.iter()` and append retry jobs to the same `Vec` inside the loop, but Rust rejects the mutation. How would you preserve the intended behavior and avoid an accidental endless loop?
resilienceiteration - 53
A helper builds a `String` locally and tries to return `&str` to avoid an allocation at the caller, producing a lifetime error. What return type and API changes would you consider?
lifetimes - 54
A `ServiceConfig<'a>` stores `&'a str` fields parsed from a temporary request body, but the service must keep the config after the request ends. How would you redesign the boundary?
config - 55
You want a parser result struct to own its source `String` and also cache several `&str` slices into that same string. How would you design it safely?
error-handlingtypesdesign - 56
A `move` closure registered as a long-lived callback consumes `config`, but the setup code still needs `config` afterward. How would you fix the capture without cloning unnecessary state?
closurescallbacksconfig - 57
After moving `request.body: String` into a decoder, code tries to log the whole `request` and gets a partial-move error. How would you choose among borrowing, restructuring, and replacing the field?
ownership - 58
A request counter first calls `contains_key`, then `get_mut`, and otherwise inserts a new value into a `HashMap`, causing duplicate lookups and awkward borrows. How would you rewrite it?
ownership - 59
A method needs mutable access to two independent struct fields, and another function must swap two elements of one slice, but straightforward indexing triggers overlapping mutable-borrow errors. How would you split these borrows safely?
ownershiptypesindexes - 60
An async method reads an item from `self.cache`, awaits a network response, and then updates the cache. How would you avoid a borrow across `await` and prevent applying a stale response?
asyncownershipcaching - 61
A safe Rust service stops making progress under load, but no race is reported; how would you investigate and fix it?
debugging - 62
A function locks a std::sync::Mutex and calls a helper that locks the same mutex, causing a hang; how would you diagnose and fix it?
concurrency - 63
Two threads transfer values between accounts protected by separate mutexes and occasionally deadlock; how would you implement a safe locking strategy?
concurrencylocking - 64
A cache mutex returns PoisonError after a worker panics during an update; how would you recover without hiding corrupted state?
concurrencyerrorscaching - 65
Profiling shows many workers waiting on one Arc<Mutex<HashMap<K, V>>>; how would you diagnose the contention and redesign it?
concurrencyprofiling - 66
You are implementing a concurrent job scheduler; when would you use channels instead of shared mutex-protected state, and how would you combine them?
concurrency - 67
thread::spawn rejects a closure capturing Rc<RefCell<State>> because it is not Send; how would you understand and fix the compiler error?
closuresconcurrencysmart-pointers - 68
Tokio refuses to spawn a future that holds std::sync::MutexGuard across an await, and the code can also stall the runtime; how would you fix it?
asyncconcurrencyasync-runtime - 69
A Tokio ingestion pipeline consumes memory when producers outrun a slow worker; how would you add bounded-channel backpressure and handle overload?
backpressurememoryci-cd - 70
A Tokio operation times out while holding a temporary file and a remote lease; how would you implement cancellation and cleanup correctly?
resilienceasync-runtime - 71
A Rust service has become slower after a feature release; how would you diagnose it before changing the code?
- 72
A hot loop creates several temporary strings for every record; how would you reduce heap allocations while preserving behavior?
data-structures - 73
A text-normalization function currently clones every input String even though most values are unchanged; how would you redesign it using borrowing or Cow?
ownershipnormalization - 74
How would you implement a zero-copy parser whose parsed fields refer to slices of the original input?
ownershiptypes - 75
How would you choose between String and &str when designing a reusable Rust text-processing API?
designapiconcurrency - 76
A batch processor repeatedly grows and discards Vec buffers; how would you optimize capacity and reuse without wasting memory?
capacitybatchoptimization - 77
A teammate claims a manual loop is always faster than Rust iterators; how would you decide which implementation to keep?
iterationiterators - 78
Profiling attributes meaningful time to bounds checks in a numeric slice loop. How would you optimize it safely before considering `get_unchecked`?
optimizationprofilingtypes - 79
How would you write a trustworthy Rust microbenchmark for an optimization and use black_box correctly?
optimization - 80
Profiling shows many cache misses while processing a large collection of records; how would you improve data layout or batching?
cachingprofilingbatch - 81
You are designing an AST whose nodes contain child nodes, and one rare variant carries a very large payload. How would you use Box without hiding the real ownership requirements?
ownershipdesign - 82
A single-threaded editor model needs multiple UI objects to share and mutate nodes in a graph. Would you accept Rc<RefCell<Node>>, and how would you control its failure modes?
concurrencysmart-pointers - 83
A configuration object starts in a current-thread prototype but will now be read by handlers launched with tokio::spawn. Should the team keep Rc or switch to Arc?
configasync-runtimeprototypes - 84
Your Rc-based tree keeps children alive from the parent and also stores a parent pointer in every child, so removed subtrees never drop. How would you fix the design?
designownership - 85
A service executes one of several request processors, and third-party crates may add processors later. When would you choose Box<dyn Processor> instead of an enum or generics?
genericsconcurrencyenums - 86
A filesystem API must handle paths that may not be valid UTF-8. How would you choose between `&Path`, `PathBuf`, `&str`, and `String`?
api - 87
You are publishing a Rust library that can fail with validation, parsing, and I/O errors, and callers need to react differently to each. How would you design the error type?
reactdesignvalidation - 88
A Rust command-line application orchestrates configuration loading, HTTP calls, and file writes. Where would anyhow help, and where could careless use make recovery harder?
orchestrationhttpconfig - 89
A job lookup returns Option<&Job>, but the API operation requires that the requested job exist. How should this boundary handle None?
error-handling - 90
A worker sees timeouts, HTTP 429 responses, invalid credentials, and malformed jobs. How would you decide which failures to retry?
resiliencehttp - 91
A Rust crate has complex private parsing helpers and a small public API. What would you cover with unit tests versus integration tests?
unitintegrationapi - 92
How would you add property-based tests to a Rust parser after example-based tests missed malformed input?
testing - 93
A Tokio test for retries and timeouts is slow and flaky. How would you make it deterministic?
resilienceflakyasync-runtime - 94
When would you use loom to test concurrent Rust code, and how would you keep the model useful?
concurrency - 95
How would you fuzz a Rust parser that passes untrusted bytes into a small unsafe decoding boundary?
unsafe - 96
A Rust service must handle disk errors and transient HTTP failures. How would you test those paths without making tests unreliable?
http - 97
How would you use Rust doctests for a public library without turning documentation into fragile test code?
documentation - 98
How would you make cargo clippy a useful CI gate without blindly denying every available lint?
cargo - 99
A Tokio worker panics in CI, but the log only says that a task failed. How would you diagnose it?
async-runtimeerrors - 100
You receive a production input that triggers a Rust parser bug. How do you reproduce the issue and prevent a regression?