yoklainterview sim

Rust Backend Interview Questions

450 verified Rust Backend interview questions — solve with answers, learn from explanations, test yourself in a real simulation.

Try the real simulation →

Sample questions

Rs Async Runtime FuturesDifficulty 1
async fn work() {
    println!("work ran");
}

#[tokio::main]
async fn main() {
    let f = work();
    println!("created future");
    f.await;
}

What does this program print?
  • awork ran then created future, the body starting right at the call
  • bcreated future, then work ran
  • cOnly created future; the future is dropped unpolled
  • dIt does not compile; a future must always be awaited where it is created
Explanation:Calling an async fn builds a future and does nothing else. The body runs only when the future is polled, which happens at f.await. So the println! in main executes first, and work ran appears afterwards. Measured on Rust 1.98 / tokio 1.x: created future precedes work ran.
Rs Async Runtime FuturesDifficulty 1
async fn fetch() -> i32 { 1 }

fn main() {
    let x = fetch().await;
    println!("{x}");
}

What is the outcome of building this file?
  • aPrints 1; a runtime is created implicitly for the .await
  • bIt compiles, then panics at runtime with there is no reactor running
  • cPrints nothing; the future is dropped without ever being polled
  • dCompile error E0728: await outside an async context
Explanation:.await is a language construct that can only appear inside an async context. A plain fn main is synchronous, so rustc rejects the file with E0728 before any runtime question arises. To drive the future from a synchronous main you need an executor, for example #[tokio::main] or an explicit Runtime::block_on.
Rs Async Runtime FuturesDifficulty 1
What does the #[tokio::main] attribute actually turn an async fn main into?
  • aA special entry point that the operating system's loader drives asynchronously
  • bA synchronous fn main that constructs a Runtime and calls block_on on the original async body
  • cA function that spawns the body onto a background thread and returns to the OS immediately
  • dA main registered with the compiler's built-in executor, which polls it on the main thread
Explanation:Rust has no built-in executor, and an async fn main is rejected by the compiler with E0752. The macro rewrites the function into an ordinary synchronous main that constructs a Runtime (multi-threaded by default) and blocks on the original body. Nothing about it is special to the OS or the compiler.
Rs Async Runtime FuturesDifficulty 2
use std::time::Duration;

async fn a() {
    tokio::time::sleep(Duration::from_millis(50)).await;
    println!("a done");
}
async fn b() {
    tokio::time::sleep(Duration::from_millis(10)).await;
    println!("b done");
}

#[tokio::main]
async fn main() {
    tokio::join!(a(), b());
    println!("both");
}

In what order do the lines appear?
  • ab done, a done, both; both sleeps run at once
  • ba done, b done, both; join! polls its arguments in the order written
  • cboth, then a done and b done in a nondeterministic order
  • dCompilation fails: join! accepts only JoinHandles returned by tokio::spawn
Explanation:join! polls all of its futures on the current task and completes when every one is done. Both sleeps start at the same time, so the 10 ms one finishes first and prints b done, then a done after 50 ms, and both last. The order of arguments does not serialize them.
Rs Async Runtime FuturesDifficulty 2
#[tokio::main]
async fn main() {
    let h = tokio::spawn(async { 5 });
    let v: i32 = h.await;
    println!("{v}");
}

Why does this fail to compile?
  • atokio::spawn needs an async move block; a plain async block cannot be spawned
  • bAwaiting a JoinHandle<i32> yields Result<i32, JoinError>, so a bare i32 does not match
  • cA JoinHandle cannot be awaited directly; it must be passed to tokio::join!
  • dThe spawned block returns () because 5 has no trailing semicolon
Explanation:A spawned task can panic or be cancelled, so awaiting its JoinHandle<T> produces Result<T, JoinError>. rustc reports E0308 (mismatched types) and even suggests .expect(...). Writing h.await.unwrap() or propagating with ? fixes it. The block returns 5 just fine; a plain async block with no captures is spawnable.
Rs Async Runtime FuturesDifficulty 2
use std::time::Duration;

#[tokio::main]
async fn main() {
    let h = tokio::spawn(async {
        tokio::time::sleep(Duration::from_millis(20)).await;
        println!("task finished");
    });
    drop(h);
    tokio::time::sleep(Duration::from_millis(60)).await;
    println!("main done");
}

What is printed?
  • atask finished then main done; the task keeps running detached
  • bOnly main done; dropping the JoinHandle cancels the task
  • cOnly main done; a task whose handle is gone is never polled again
  • dA panic: a JoinHandle must be awaited or aborted before it is dropped
Explanation:Dropping a JoinHandle detaches the task; it keeps running on the runtime. Measured output is task finished followed by main done. Cancellation requires an explicit abort(), or dropping the whole runtime. This is a deliberate difference from dropping an un-spawned future, which does cancel it.

Test yourself against the 3750-question Backend bank.

Start interview