Advanced Rust Interview Questions: Analysis and Examples

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

How to Pass a Rust Interview: Top Advanced-Level Questions



Rust is one of the most in-demand systems programming languages, and the demand for senior developers continues to grow. If you have already mastered the basic syntax and ownership system, the interview will feature questions that test a deep understanding of unsafe code, asynchrony, complex lifetimes, and metaprogramming. In this article, we will break down key topics and provide code examples to help you confidently pass the interview.



1. Unsafe Rust: When and Why?



One of the most common questions in an advanced interview is: "Explain when you would use unsafe and what guarantees are violated."



The answer should include the four main capabilities that unsafe provides:



  • Dereferencing raw pointers (*const T and *mut T)
  • Calling unsafe functions (e.g., FFI)
  • Accessing mutable static variables
  • Implementing unsafe traits


Example: implementing a simple dynamic array with manual memory management.



use std::alloc::{alloc, dealloc, Layout};use std::ptr;

struct MyVec { ptr: *mut i32, len: usize, cap: usize,}

impl MyVec { fn new() -> Self { MyVec { ptr: ptr::null_mut(), len: 0, cap: 0, } }

fn push(&mut self, val: i32) { if self.len == self.cap { let new_cap = if self.cap == 0 { 1 } else { self.cap * 2 }; let new_layout = Layout::array::<i32>(new_cap).unwrap(); let new_ptr = unsafe { alloc(new_layout) as *mut i32 }; // copy old data unsafe { ptr::copy_nonoverlapping(self.ptr, new_ptr, self.len); if !self.ptr.is_null() { let old_layout = Layout::array::<i32>(self.cap).unwrap(); dealloc(self.ptr as *mut u8, old_layout); } } self.ptr = new_ptr; self.cap = new_cap; } unsafe { ptr::write(self.ptr.add(self.len), val); } self.len += 1; }}

impl Drop for MyVec { fn drop(&mut self) { if !self.ptr.is_null() { unsafe { let layout = Layout::array::<i32>(self.cap).unwrap(); dealloc(self.ptr as *mut u8, layout); } } }}


Tip: always emphasize that unsafe is not about disabling checks, but about taking responsibility for invariants.



2. Lifetimes and Variance: How the Compiler Checks References



At an advanced level, you will be asked to explain what variance (covariance, contravariance, invariance) means in the context of lifetimes. This is key to understanding why some types do not compile.



  • Covariance: &'a T — if 'a is longer than 'b, then &'a T can be used as &'b T.
  • Contravariance: fn(T) — if 'a is longer than 'b, then fn(&'b T) can be used as fn(&'a T).
  • Invariance: &mut T — the lifetime must match exactly.


Example: why does the following code not compile?



fn foo<'a>(x: &'a mut &'a str, y: &'a str) {    *x = y;}

fn main() { let mut s = "hello"; let r = &s; foo(&mut s, r); // error!}


Answer: &mut T is invariant over T, so the lifetime of the inner reference must match the lifetime of the outer reference. The compiler cannot "shorten" the lifetime of r.



3. Async Rust: Pin, Unpin and Future

Blogs

Book Recommendations