Cau hoi phong van Rust: Huong dan day du 2026

25 cau hoi phong van Rust thuong gap nhat. Ownership, borrowing, lifetime, trait, async va concurrency voi cau tra loi chi tiet cung vi du code.

Cau hoi phong van Rust - Huong dan day du

Phong van Rust danh gia kha nang hieu biet ve he thong ownership doc dao, quan ly bo nho khong can garbage collector, va kha nang viet code concurrent an toan. Huong dan nay bao gom cac cau hoi thiet yeu, tu co ban ve ownership den cac pattern async va concurrency nang cao.

Meo phong van

Nguoi phong van danh gia cao nhung giai thich the hien su hieu biet ve dam bao an toan bo nho cua Rust. Giai thich cach compiler ngan chan loi tai thoi diem bien dich tao nen su khac biet.

Ownership va Borrowing

Cau hoi 1: Giai thich he thong ownership cua Rust

Ownership la khai niem trung tam cua Rust, cho phep quan ly bo nho khong can garbage collector dong thoi dam bao an toan bo nho tai thoi diem bien dich.

ownership_basics.rsrust
// The three fundamental rules of ownership

fn main() {
    // Rule 1: Each value has a single owner
    let s1 = String::from("hello");  // s1 is the owner

    // Rule 2: Only one variable can own a value at a time
    let s2 = s1;  // s1 is MOVED to s2
    // println!("{}", s1);  // ERROR: s1 is no longer valid
    println!("{}", s2);  // OK: s2 is now the owner

    // Rule 3: When the owner goes out of scope, the value is dropped
    {
        let s3 = String::from("world");
        // s3 is valid here
    }  // s3 goes out of scope, memory is automatically freed

    // Copy types: simple types are copied, not moved
    let x = 5;
    let y = x;  // x is COPIED, not moved
    println!("x = {}, y = {}", x, y);  // Both are valid
}

// Move in action with functions
fn take_ownership(s: String) {
    // s takes ownership of the String
    println!("{}", s);
}  // s is dropped here, memory freed

fn makes_copy(i: i32) {
    // i is a copy of the argument
    println!("{}", i);
}  // i goes out of scope, nothing special (Copy type)

fn ownership_with_functions() {
    let s = String::from("hello");
    take_ownership(s);  // s is moved into the function
    // println!("{}", s);  // ERROR: s is no longer valid

    let x = 5;
    makes_copy(x);  // x is copied
    println!("{}", x);  // OK: x is still valid
}

Ownership loai bo cac loi bo nho pho bien: use-after-free, double-free va memory leak. Compiler dam bao cac thuoc tinh nay tai thoi diem bien dich.

Cau hoi 2: Su khac biet giua immutable va mutable borrowing la gi?

Borrowing cho phep su dung gia tri ma khong lay ownership, voi cac quy tac nghiem ngat de ngan chan data race.

borrowing_rules.rsrust
// Immutable and mutable references

fn main() {
    let mut s = String::from("hello");

    // IMMUTABLE REFERENCES (&T)
    // Can coexist in unlimited numbers
    let r1 = &s;  // immutable reference
    let r2 = &s;  // another immutable reference
    println!("{} and {}", r1, r2);  // OK

    // MUTABLE REFERENCE (&mut T)
    // Only one at a time, and no simultaneous immutable references
    let r3 = &mut s;  // mutable reference
    // let r4 = &s;  // ERROR: cannot have both immutable and mutable
    // let r5 = &mut s;  // ERROR: only one mutable reference allowed
    r3.push_str(" world");
    println!("{}", r3);

    // Reference scopes are limited to their last use
    let r6 = &s;  // OK because r3 is no longer used
    println!("{}", r6);
}

// Practical example: modifying a struct
struct User {
    name: String,
    age: u32,
}

impl User {
    // &self: read-only access
    fn get_name(&self) -> &str {
        &self.name
    }

    // &mut self: modification access
    fn set_name(&mut self, name: String) {
        self.name = name;
    }

    // self: takes ownership (consumes the instance)
    fn into_name(self) -> String {
        self.name  // The User instance no longer exists after this
    }
}

fn borrowing_with_structs() {
    let mut user = User {
        name: String::from("Alice"),
        age: 30,
    };

    // Reading
    println!("Name: {}", user.get_name());

    // Modifying
    user.set_name(String::from("Bob"));

    // Consuming
    let name = user.into_name();
    // user.age;  // ERROR: user has been consumed
}

Cac quy tac nay dam bao khong co data race tai thoi diem bien dich. Khong ngon ngu nao khac cung cap dam bao nay ma khong hy sinh hieu suat.

Non-Lexical Lifetimes

Tu Rust 2018, compiler su dung NLL (Non-Lexical Lifetimes) de xac dinh chinh xac hon khi nao reference khong con duoc su dung, cho phep linh hoat hon.

Cau hoi 3: Lifetime la gi va khi nao can annotation?

Lifetime la cac annotation cho compiler biet reference co hieu luc bao lau, ngan chan dangling reference.

lifetimes.rsrust
// Understanding and annotating lifetimes

// ERROR: dangling reference
// fn dangling() -> &String {
//     let s = String::from("hello");
//     &s  // s is dropped at function end, reference invalid
// }

// The compiler often infers lifetimes automatically
fn first_word(s: &str) -> &str {
    // Elided lifetime: compiler understands the return
    // has the same lifetime as the input
    match s.find(' ') {
        Some(i) => &s[..i],
        None => s,
    }
}

// Explicit annotation needed with multiple references
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    // 'a means: the return lives at least as long
    // as the shorter of the two inputs
    if x.len() > y.len() { x } else { y }
}

fn lifetime_example() {
    let string1 = String::from("long string");
    let result;

    {
        let string2 = String::from("xyz");
        result = longest(&string1, &string2);
        println!("Longest: {}", result);  // OK here
    }
    // println!("{}", result);  // ERROR: string2 is dropped
}

// Lifetimes in structs
struct ImportantExcerpt<'a> {
    part: &'a str,  // Struct cannot outlive part
}

impl<'a> ImportantExcerpt<'a> {
    // Method returning a reference with the same lifetime
    fn level(&self) -> i32 {
        3
    }

    // Elided lifetime for &self returning a new reference
    fn announce_and_return_part(&self, announcement: &str) -> &str {
        println!("Attention: {}", announcement);
        self.part  // Returns with lifetime 'a
    }
}

// Static lifetime: lives for the entire program duration
fn static_lifetime() {
    let s: &'static str = "hello";  // Stored in the binary

    // Constants have implicit 'static lifetime
    const MAX_POINTS: u32 = 100_000;
}

// Combining lifetimes and generics
fn longest_with_announcement<'a, T>(
    x: &'a str,
    y: &'a str,
    ann: T,
) -> &'a str
where
    T: std::fmt::Display,
{
    println!("Announcement: {}", ann);
    if x.len() > y.len() { x } else { y }
}

Lifetime duoc xac minh tai thoi diem bien dich. Neu code bien dich thanh cong, cac reference duoc dam bao la hop le.

Trait va Generics

Cau hoi 4: Trait hoat dong nhu the nao trong Rust?

Trait dinh nghia hanh vi chung giua cac kieu du lieu khac nhau, tuong tu interface nhung voi nhieu tinh nang bo sung.

traits_basics.rsrust
// Defining and implementing traits

// Trait definition
trait Summary {
    // Required method (no body)
    fn summarize(&self) -> String;

    // Method with default implementation
    fn summarize_author(&self) -> String {
        String::from("(Anonymous)")
    }

    // Default method that calls a required method
    fn full_summary(&self) -> String {
        format!("By {} - {}", self.summarize_author(), self.summarize())
    }
}

// Implementation for different types
struct NewsArticle {
    headline: String,
    location: String,
    author: String,
    content: String,
}

impl Summary for NewsArticle {
    fn summarize(&self) -> String {
        format!("{}, by {} ({})", self.headline, self.author, self.location)
    }

    fn summarize_author(&self) -> String {
        format!("@{}", self.author)
    }
}

struct Tweet {
    username: String,
    content: String,
    reply: bool,
    retweet: bool,
}

impl Summary for Tweet {
    fn summarize(&self) -> String {
        format!("{}: {}", self.username, self.content)
    }
}

// Trait bounds: constraining generics
fn notify<T: Summary>(item: &T) {
    println!("Breaking news! {}", item.summarize());
}

// Alternative syntax with where
fn notify_verbose<T>(item: &T)
where
    T: Summary,
{
    println!("Breaking news! {}", item.summarize());
}

// Multiple trait bounds
fn notify_complex<T: Summary + Clone + std::fmt::Display>(item: &T) {
    println!("{}", item);
}

// Return a type that implements a trait
fn create_summarizable() -> impl Summary {
    Tweet {
        username: String::from("rust_lang"),
        content: String::from("Rust 2026 is amazing!"),
        reply: false,
        retweet: false,
    }
}

Trait cho phep da hinh (polymorphism) ma khong can ke thua lop, uu tien composition hon inheritance.

Cau hoi 5: Giai thich su khac biet giua generic tinh va dong

Rust cung cap hai cach tiep can cho da hinh: monomorphization (tinh) va trait object (dong).

static_vs_dynamic_dispatch.rsrust
// Static vs dynamic dispatch

trait Animal {
    fn speak(&self) -> String;
    fn name(&self) -> &str;
}

struct Dog { name: String }
struct Cat { name: String }

impl Animal for Dog {
    fn speak(&self) -> String { String::from("Woof!") }
    fn name(&self) -> &str { &self.name }
}

impl Animal for Cat {
    fn speak(&self) -> String { String::from("Meow!") }
    fn name(&self) -> &str { &self.name }
}

// STATIC DISPATCH (monomorphization)
// Compiler generates a version for each concrete type
fn make_speak_static<T: Animal>(animal: &T) {
    // At compile time, becomes make_speak_Dog and make_speak_Cat
    println!("{} says {}", animal.name(), animal.speak());
}

// Advantages: inlining possible, no runtime overhead
// Disadvantages: larger binary, type must be known at compile time

// DYNAMIC DISPATCH (trait objects)
// Uses a vtable to resolve methods at runtime
fn make_speak_dynamic(animal: &dyn Animal) {
    // Resolved via a pointer table (vtable) at runtime
    println!("{} says {}", animal.name(), animal.speak());
}

// Advantages: can store different types, smaller binary
// Disadvantages: indirection overhead, no inlining

fn main() {
    let dog = Dog { name: String::from("Rex") };
    let cat = Cat { name: String::from("Whiskers") };

    // Static: type is known at compile time
    make_speak_static(&dog);
    make_speak_static(&cat);

    // Dynamic: type is resolved at runtime
    make_speak_dynamic(&dog);
    make_speak_dynamic(&cat);

    // Heterogeneous collection (requires dynamic dispatch)
    let animals: Vec<Box<dyn Animal>> = vec![
        Box::new(Dog { name: String::from("Buddy") }),
        Box::new(Cat { name: String::from("Luna") }),
    ];

    for animal in animals.iter() {
        println!("{} says {}", animal.name(), animal.speak());
    }
}

// Object safety: not all traits can become trait objects
trait ObjectSafe {
    fn method(&self);
    // No Self in return type
    // No generic parameters
}

// NOT object safe (cannot be dyn NotObjectSafe)
trait NotObjectSafe {
    fn create() -> Self;  // Self in return
    fn generic<T>(&self, t: T);  // Generic
}

Static dispatch duoc uu tien cho hieu suat. Dynamic dispatch huu ich cho cac collection khong dong nhat va tinh linh hoat.

Sẵn sàng chinh phục phỏng vấn Rust?

Luyện tập với mô phỏng tương tác, flashcards và bài kiểm tra kỹ thuật.

Xu ly loi

Cau hoi 6: Xu ly loi voi Result va Option nhu the nao?

Rust khong co exception. Xu ly loi duoc thuc hien thong qua cac kieu Result<T, E> va Option<T> voi pattern matching.

error_handling.rsrust
// Idiomatic error handling in Rust

use std::fs::File;
use std::io::{self, Read};

// Option<T>: presence or absence of a value
fn find_user(id: u32) -> Option<String> {
    match id {
        1 => Some(String::from("Alice")),
        2 => Some(String::from("Bob")),
        _ => None,  // No user found
    }
}

// Result<T, E>: success or error
fn divide(a: f64, b: f64) -> Result<f64, String> {
    if b == 0.0 {
        Err(String::from("Division by zero"))
    } else {
        Ok(a / b)
    }
}

fn option_combinators() {
    let user = find_user(1);

    // Pattern matching
    match user {
        Some(name) => println!("Found: {}", name),
        None => println!("Not found"),
    }

    // unwrap_or: default value
    let name = find_user(99).unwrap_or(String::from("Unknown"));

    // map: transform the value if present
    let upper = find_user(1).map(|n| n.to_uppercase());

    // and_then (flatMap): chain Options
    let first_char = find_user(1).and_then(|n| n.chars().next());

    // if let: simplified pattern matching
    if let Some(name) = find_user(2) {
        println!("User 2 is {}", name);
    }
}

fn result_handling() -> Result<(), Box<dyn std::error::Error>> {
    // The ? operator propagates errors automatically
    let result = divide(10.0, 2.0)?;
    println!("Result: {}", result);

    // Equivalent to:
    // let result = match divide(10.0, 2.0) {
    //     Ok(v) => v,
    //     Err(e) => return Err(e.into()),
    // };

    Ok(())
}

// File reading with error propagation
fn read_file_contents(path: &str) -> Result<String, io::Error> {
    let mut file = File::open(path)?;  // Propagates error if failure
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
    Ok(contents)
}

// Custom errors
#[derive(Debug)]
enum AppError {
    IoError(io::Error),
    ParseError(String),
    NotFound(String),
}

impl std::fmt::Display for AppError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            AppError::IoError(e) => write!(f, "IO error: {}", e),
            AppError::ParseError(s) => write!(f, "Parse error: {}", s),
            AppError::NotFound(s) => write!(f, "Not found: {}", s),
        }
    }
}

impl std::error::Error for AppError {}

// Automatic conversion with From
impl From<io::Error> for AppError {
    fn from(error: io::Error) -> Self {
        AppError::IoError(error)
    }
}

fn complex_operation() -> Result<String, AppError> {
    let contents = std::fs::read_to_string("config.txt")?;  // Auto-convert

    if contents.is_empty() {
        return Err(AppError::NotFound(String::from("Config is empty")));
    }

    Ok(contents)
}

Toan tu ? giup code ngan gon dong thoi bat buoc xu ly loi mot cach ro rang. Khong co bat ngo nao khi runtime.

Tranh unwrap trong production

unwrap() va expect() se panic neu gia tri la None hoac Err. Chi su dung cho prototyping hoac truong hop khong the that bai. Trong production, nen dung propagation voi ? hoac combinator.

Cau hoi 7: Tao custom error voi thiserror nhu the nao?

Crate thiserror don gian hoa viec tao custom error than thien voi nguoi dung.

custom_errors.rsrust
// Custom errors with thiserror

use thiserror::Error;

// Error definition with derive macro
#[derive(Error, Debug)]
pub enum DataStoreError {
    #[error("connection failed: {0}")]
    ConnectionFailed(String),

    #[error("query failed: {query}")]
    QueryFailed { query: String, source: std::io::Error },

    #[error("record not found: id={id}")]
    NotFound { id: u64 },

    #[error("invalid data: {0}")]
    InvalidData(#[from] serde_json::Error),

    #[error(transparent)]  // Delegates Display to source
    Other(#[from] anyhow::Error),
}

// Implementation with rich context
pub struct DataStore {
    connection_string: String,
}

impl DataStore {
    pub fn connect(conn_str: &str) -> Result<Self, DataStoreError> {
        if conn_str.is_empty() {
            return Err(DataStoreError::ConnectionFailed(
                "Empty connection string".into()
            ));
        }
        Ok(Self { connection_string: conn_str.to_string() })
    }

    pub fn get_record(&self, id: u64) -> Result<Record, DataStoreError> {
        // Query simulation
        if id == 0 {
            return Err(DataStoreError::NotFound { id });
        }
        Ok(Record { id, data: format!("Record {}", id) })
    }
}

pub struct Record {
    pub id: u64,
    pub data: String,
}

// Usage with anyhow for applications
use anyhow::{Context, Result};

fn application_code() -> Result<()> {
    let store = DataStore::connect("postgres://localhost/db")
        .context("Failed to connect to database")?;

    let record = store.get_record(42)
        .context("Failed to fetch user record")?;

    println!("Got: {}", record.data);
    Ok(())
}

// Pattern: converting errors with context
fn read_config() -> Result<Config> {
    let contents = std::fs::read_to_string("config.toml")
        .context("Failed to read config file")?;

    let config: Config = toml::from_str(&contents)
        .context("Failed to parse config file")?;

    Ok(config)
}

#[derive(Debug)]
struct Config {
    // ...
}

thiserror ly tuong cho library (error co kieu), trong khi anyhow phu hop cho ung dung (linh hoat toi da).

Smart Pointer

Cau hoi 8: Giai thich Box, Rc, Arc va RefCell

Smart pointer quan ly bo nho heap va cho phep cac pattern ma ownership thong thuong khong the thuc hien.

smart_pointers.rsrust
// Main smart pointers in Rust

use std::rc::Rc;
use std::sync::Arc;
use std::cell::RefCell;

// BOX<T>: heap allocation
// Used for: recursive types, large types, trait objects
fn box_example() {
    // Simple heap allocation
    let b = Box::new(5);
    println!("b = {}", b);

    // Recursive type (impossible without Box)
    #[derive(Debug)]
    enum List {
        Cons(i32, Box<List>),
        Nil,
    }

    let list = List::Cons(1,
        Box::new(List::Cons(2,
            Box::new(List::Cons(3,
                Box::new(List::Nil))))));
    println!("{:?}", list);
}

// RC<T>: Reference Counting (single-threaded)
// Multiple owners for the same data
fn rc_example() {
    let data = Rc::new(vec![1, 2, 3]);

    // Clone increments the reference counter
    let data_clone1 = Rc::clone(&data);  // count = 2
    let data_clone2 = Rc::clone(&data);  // count = 3

    println!("Reference count: {}", Rc::strong_count(&data));  // 3

    // Each clone can read the data
    println!("data_clone1: {:?}", data_clone1);

    // Data is freed when the last Rc is dropped
}

// ARC<T>: Atomic Reference Counting (thread-safe)
// Like Rc but usable across threads
fn arc_example() {
    use std::thread;

    let data = Arc::new(vec![1, 2, 3, 4, 5]);
    let mut handles = vec![];

    for i in 0..3 {
        let data_clone = Arc::clone(&data);
        let handle = thread::spawn(move || {
            // Each thread has its own Arc
            println!("Thread {}: {:?}", i, data_clone);
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }
}

// REFCELL<T>: Interior Mutability
// Allows mutation even with an immutable reference
fn refcell_example() {
    let data = RefCell::new(5);

    // borrow() returns an immutable reference
    println!("Value: {}", *data.borrow());

    // borrow_mut() returns a mutable reference
    *data.borrow_mut() += 1;
    println!("After mutation: {}", *data.borrow());

    // Borrowing rules are checked at RUNTIME
    // Panics if rules are violated
    // let r1 = data.borrow();
    // let r2 = data.borrow_mut();  // PANIC: already borrowed
}

// Common combination: Rc<RefCell<T>>
// Multiple owners with possible mutation
fn rc_refcell_example() {
    #[derive(Debug)]
    struct Node {
        value: i32,
        children: Vec<Rc<RefCell<Node>>>,
    }

    let node1 = Rc::new(RefCell::new(Node {
        value: 1,
        children: vec![],
    }));

    let node2 = Rc::new(RefCell::new(Node {
        value: 2,
        children: vec![Rc::clone(&node1)],  // node1 is child of node2
    }));

    // Modify node1 from anywhere
    node1.borrow_mut().value = 10;

    println!("node2 child value: {}",
        node2.borrow().children[0].borrow().value);  // 10
}

// For threads: Arc<Mutex<T>> or Arc<RwLock<T>>
fn arc_mutex_example() {
    use std::sync::Mutex;
    use std::thread;

    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for _ in 0..10 {
        let counter = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            let mut num = counter.lock().unwrap();
            *num += 1;
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("Final count: {}", *counter.lock().unwrap());  // 10
}

Chon smart pointer phu hop theo ngu canh: Box cho heap don gian, Rc/Arc de chia se, RefCell/Mutex cho interior mutability.

Concurrency

Cau hoi 9: Rust dam bao an toan thread nhu the nao?

He thong kieu cua Rust ngan chan data race tai thoi diem bien dich thong qua cac trait Send va Sync.

thread_safety.rsrust
// Concurrent safety guarantees

use std::thread;
use std::sync::{Arc, Mutex, mpsc};

// SEND: a type can be transferred to another thread
// SYNC: a type can be shared between threads via references

// Most types are Send and Sync automatically
// Exceptions: Rc (not Send/Sync), RefCell (not Sync), raw pointers

fn send_example() {
    let data = vec![1, 2, 3];

    // Vec is Send, so it can be moved to another thread
    let handle = thread::spawn(move || {
        println!("Data in thread: {:?}", data);
    });

    handle.join().unwrap();
}

// The compiler prevents concurrency errors
fn compile_time_safety() {
    // This would NOT compile:
    // let data = std::rc::Rc::new(5);
    // thread::spawn(move || {
    //     println!("{}", data);  // ERROR: Rc is not Send
    // });

    // Solution: use Arc
    let data = Arc::new(5);
    let data_clone = Arc::clone(&data);

    thread::spawn(move || {
        println!("{}", data_clone);  // OK: Arc is Send
    });
}

// Mutex for thread-safe shared mutation
fn mutex_pattern() {
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for _ in 0..10 {
        let counter = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            // lock() blocks until exclusive access is obtained
            let mut num = counter.lock().unwrap();
            *num += 1;
            // MutexGuard is dropped here, releasing the lock
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("Result: {}", *counter.lock().unwrap());
}

// RwLock for multiple reads / exclusive write
fn rwlock_example() {
    use std::sync::RwLock;

    let data = Arc::new(RwLock::new(vec![1, 2, 3]));
    let mut handles = vec![];

    // Multiple simultaneous readers
    for i in 0..3 {
        let data = Arc::clone(&data);
        handles.push(thread::spawn(move || {
            let read = data.read().unwrap();
            println!("Reader {}: {:?}", i, *read);
        }));
    }

    // Only one writer at a time
    {
        let data = Arc::clone(&data);
        handles.push(thread::spawn(move || {
            let mut write = data.write().unwrap();
            write.push(4);
            println!("Writer added 4");
        }));
    }

    for handle in handles {
        handle.join().unwrap();
    }
}

// Channels for inter-thread communication
fn channel_example() {
    let (tx, rx) = mpsc::channel();  // Multi-producer, single-consumer

    // Clone the sender for multiple producers
    let tx1 = tx.clone();
    thread::spawn(move || {
        tx1.send("from thread 1").unwrap();
    });

    thread::spawn(move || {
        tx.send("from thread 2").unwrap();
    });

    // Receive messages
    for received in rx {
        println!("Got: {}", received);
    }
}

"Fearless concurrency": neu code bien dich thanh cong, khong co data race. Compiler la tuyen phong thu dau tien.

Mutex Poisoning

Neu thread bi panic khi dang giu Mutex, Mutex se bi "poisoned". Cac loi goi lock() tiep theo tra ve error co the phuc hoi bang into_inner().

Cau hoi 10: Async/await hoat dong nhu the nao trong Rust?

Async trong Rust dua tren zero-cost Futures, khong co runtime tich hop san trong ngon ngu.

async_await.rsrust
// Asynchronous programming in Rust

use tokio::time::{sleep, Duration};

// async fn returns a Future that must be executed
async fn fetch_data(url: &str) -> Result<String, reqwest::Error> {
    // await suspends execution without blocking the thread
    let response = reqwest::get(url).await?;
    let body = response.text().await?;
    Ok(body)
}

// Futures are lazy: nothing executes without await or poll
async fn lazy_example() {
    let future = async {
        println!("This won't print yet");
    };
    // Nothing happened

    future.await;  // Now it executes
}

// Parallel execution of futures
async fn parallel_execution() {
    // join! executes multiple futures in parallel
    let (result1, result2) = tokio::join!(
        fetch_data("https://api.example.com/1"),
        fetch_data("https://api.example.com/2"),
    );

    println!("Results: {:?}, {:?}", result1, result2);
}

// select! for the first completed future
async fn race_example() {
    tokio::select! {
        result = fetch_data("https://api1.example.com") => {
            println!("API 1 responded first: {:?}", result);
        }
        result = fetch_data("https://api2.example.com") => {
            println!("API 2 responded first: {:?}", result);
        }
        _ = sleep(Duration::from_secs(5)) => {
            println!("Timeout!");
        }
    }
}

// Streams: asynchronous iterators
use tokio_stream::StreamExt;

async fn stream_example() {
    let mut stream = tokio_stream::iter(vec![1, 2, 3, 4, 5]);

    while let Some(value) = stream.next().await {
        println!("Got: {}", value);
    }
}

// Spawn for background tasks
async fn spawn_tasks() {
    let handle = tokio::spawn(async {
        sleep(Duration::from_secs(1)).await;
        "Task completed"
    });

    println!("Task spawned, doing other work...");

    let result = handle.await.unwrap();
    println!("Result: {}", result);
}

// Entry point with tokio
#[tokio::main]
async fn main() {
    // The tokio runtime executes futures
    parallel_execution().await;
}

// Alternative: multi-threaded or single-threaded runtime
#[tokio::main(flavor = "current_thread")]
async fn main_single_thread() {
    // Everything runs on a single thread
}

#[tokio::main(flavor = "multi_thread", worker_threads = 4)]
async fn main_multi_thread() {
    // Pool of 4 worker threads
}

Rust async theo mo hinh "bring your own runtime": tokio, async-std, hoac smol. Su linh hoat nay cho phep toi uu hoa theo tung truong hop su dung cu the.

Sẵn sàng chinh phục phỏng vấn Rust?

Luyện tập với mô phỏng tương tác, flashcards và bài kiểm tra kỹ thuật.

Cac Pattern Nang Cao

Cau hoi 11: Giai thich pattern Builder trong Rust

Pattern Builder mang tinh idiomatic trong Rust de xay dung cac cau truc phuc tap voi nhieu field tuy chon.

builder_pattern.rsrust
// Idiomatic Builder pattern in Rust

#[derive(Debug, Clone)]
pub struct Server {
    host: String,
    port: u16,
    max_connections: usize,
    timeout_seconds: u64,
    tls_enabled: bool,
    tls_cert_path: Option<String>,
}

// Builder with consuming approach (ownership)
#[derive(Default)]
pub struct ServerBuilder {
    host: String,
    port: u16,
    max_connections: usize,
    timeout_seconds: u64,
    tls_enabled: bool,
    tls_cert_path: Option<String>,
}

impl ServerBuilder {
    pub fn new() -> Self {
        Self {
            host: String::from("localhost"),
            port: 8080,
            max_connections: 100,
            timeout_seconds: 30,
            tls_enabled: false,
            tls_cert_path: None,
        }
    }

    // Each method takes self and returns Self for chaining
    pub fn host(mut self, host: impl Into<String>) -> Self {
        self.host = host.into();
        self
    }

    pub fn port(mut self, port: u16) -> Self {
        self.port = port;
        self
    }

    pub fn max_connections(mut self, max: usize) -> Self {
        self.max_connections = max;
        self
    }

    pub fn timeout(mut self, seconds: u64) -> Self {
        self.timeout_seconds = seconds;
        self
    }

    pub fn enable_tls(mut self, cert_path: impl Into<String>) -> Self {
        self.tls_enabled = true;
        self.tls_cert_path = Some(cert_path.into());
        self
    }

    // build() consumes the builder and creates the final structure
    pub fn build(self) -> Result<Server, String> {
        if self.tls_enabled && self.tls_cert_path.is_none() {
            return Err("TLS enabled but no certificate path provided".into());
        }

        Ok(Server {
            host: self.host,
            port: self.port,
            max_connections: self.max_connections,
            timeout_seconds: self.timeout_seconds,
            tls_enabled: self.tls_enabled,
            tls_cert_path: self.tls_cert_path,
        })
    }
}

// Fluent usage
fn create_server() -> Result<Server, String> {
    ServerBuilder::new()
        .host("0.0.0.0")
        .port(443)
        .max_connections(1000)
        .timeout(60)
        .enable_tls("/etc/ssl/cert.pem")
        .build()
}

// Alternative with derive macro (typed-builder crate)
// #[derive(TypedBuilder)]
// pub struct Config {
//     #[builder(default = "localhost".to_string())]
//     host: String,
//     #[builder(default = 8080)]
//     port: u16,
// }

// Pattern with type-level validation (typestate pattern)
pub struct Unvalidated;
pub struct Validated;

pub struct Request<State = Unvalidated> {
    url: String,
    method: String,
    headers: Vec<(String, String)>,
    _state: std::marker::PhantomData<State>,
}

impl Request<Unvalidated> {
    pub fn new(url: &str) -> Self {
        Self {
            url: url.to_string(),
            method: "GET".to_string(),
            headers: vec![],
            _state: std::marker::PhantomData,
        }
    }

    pub fn method(mut self, method: &str) -> Self {
        self.method = method.to_string();
        self
    }

    // validate() changes the state type
    pub fn validate(self) -> Result<Request<Validated>, String> {
        if self.url.is_empty() {
            return Err("URL cannot be empty".into());
        }
        Ok(Request {
            url: self.url,
            method: self.method,
            headers: self.headers,
            _state: std::marker::PhantomData,
        })
    }
}

impl Request<Validated> {
    // send() is only available on validated requests
    pub async fn send(self) -> Result<Response, reqwest::Error> {
        // Implementation...
        todo!()
    }
}

struct Response;

Pattern typestate dam bao tai thoi diem bien dich rang cac thao tac nhat dinh chi co the duoc goi trong trang thai dung.

Cau hoi 12: Implement trait cho kieu ben ngoai nhu the nao?

"Orphan rule" ngan chan viec implement trait ben ngoai cho kieu ben ngoai, nhung co cac giai phap.

newtype_pattern.rsrust
// The Newtype pattern to work around the orphan rule

use std::fmt;

// ORPHAN RULE: cannot implement Display (std) for Vec (std)
// impl fmt::Display for Vec<i32> { ... }  // ERROR

// SOLUTION 1: Newtype wrapper
struct Wrapper(Vec<String>);

impl fmt::Display for Wrapper {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "[{}]", self.0.join(", "))
    }
}

fn newtype_example() {
    let w = Wrapper(vec![
        String::from("hello"),
        String::from("world"),
    ]);
    println!("{}", w);  // [hello, world]
}

// Transparent access with Deref
use std::ops::Deref;

impl Deref for Wrapper {
    type Target = Vec<String>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

fn deref_example() {
    let w = Wrapper(vec![String::from("test")]);
    println!("Length: {}", w.len());  // Calls Vec::len via Deref
}

// SOLUTION 2: Extension trait (to add methods)
trait VecExt<T> {
    fn first_or_default(&self) -> Option<&T>;
}

impl<T> VecExt<T> for Vec<T> {
    fn first_or_default(&self) -> Option<&T> {
        self.first()
    }
}

fn extension_trait_example() {
    let v = vec![1, 2, 3];
    println!("First: {:?}", v.first_or_default());
}

// Newtype with domain semantics
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct Email(String);

impl Email {
    pub fn new(email: &str) -> Result<Self, &'static str> {
        if email.contains('@') && email.contains('.') {
            Ok(Self(email.to_string()))
        } else {
            Err("Invalid email format")
        }
    }

    pub fn as_str(&self) -> &str {
        &self.0
    }
}

impl fmt::Display for Email {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
struct UserId(u64);

impl UserId {
    pub fn new(id: u64) -> Self {
        Self(id)
    }
}

// Newtypes add type safety without runtime overhead
fn process_user(id: UserId, email: Email) {
    println!("Processing user {} with email {}", id.0, email);
}

fn type_safety_example() {
    let id = UserId::new(42);
    let email = Email::new("user@example.com").unwrap();

    process_user(id, email);

    // This would not compile:
    // process_user(email, id);  // Types reversed
    // process_user(UserId::new(42), "string");  // String instead of Email
}

Newtype khong co chi phi runtime nho dam bao bieu dien bo nho dong nhat.

Cau hoi 13: Su dung procedural macro nhu the nao?

Procedural macro cho phep tao code tai thoi diem bien dich, nhu custom derive.

procedural_macros.rsrust
// Understanding procedural macros

// Proc macros are defined in a separate crate with proc-macro = true

// Crate: my_derive (Cargo.toml: proc-macro = true)
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};

// DERIVE MACRO: #[derive(MyTrait)]
#[proc_macro_derive(MyDebug)]
pub fn my_debug_derive(input: TokenStream) -> TokenStream {
    // Parse input as a type definition
    let input = parse_macro_input!(input as DeriveInput);
    let name = input.ident;

    // Generate implementation code
    let expanded = quote! {
        impl std::fmt::Debug for #name {
            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
                write!(f, stringify!(#name))
            }
        }
    };

    TokenStream::from(expanded)
}

// ATTRIBUTE MACRO: #[my_attribute]
#[proc_macro_attribute]
pub fn route(attr: TokenStream, item: TokenStream) -> TokenStream {
    // attr contains the attribute arguments
    // item contains the annotated element (function, struct, etc.)

    let method_path = attr.to_string();  // "GET, /users"
    let input = parse_macro_input!(item as syn::ItemFn);
    let fn_name = &input.sig.ident;

    let expanded = quote! {
        #input

        // Additionally generated code
        fn register_#fn_name() {
            println!("Registered route: {}", #method_path);
        }
    };

    TokenStream::from(expanded)
}

// FUNCTION-LIKE MACRO: my_macro!(...)
#[proc_macro]
pub fn make_answer(_input: TokenStream) -> TokenStream {
    "fn answer() -> u32 { 42 }".parse().unwrap()
}

// --- Usage in client code ---

// Derive macro
#[derive(MyDebug)]
struct Point {
    x: i32,
    y: i32,
}

// Attribute macro
#[route("GET", "/users")]
fn get_users() -> Vec<User> {
    vec![]
}

// Function-like macro
make_answer!();  // Generates fn answer() -> u32 { 42 }

fn main() {
    let p = Point { x: 1, y: 2 };
    println!("{:?}", p);  // Uses our MyDebug

    println!("Answer: {}", answer());  // 42
}

struct User;

Procedural macro rat huu ich cho code lap di lap lai: serialization, routing web, validation, va nhieu hon nua.

An toan bo nho va Unsafe

Cau hoi 14: Khi nao va lam sao su dung unsafe?

Block unsafe cho phep bo qua mot so kiem tra cua compiler cho code cap thap.

unsafe_rust.rsrust
// Understanding unsafe and its guarantees

// The 5 superpowers of unsafe:
// 1. Dereference raw pointers
// 2. Call unsafe functions
// 3. Access/modify mutable static variables
// 4. Implement unsafe traits
// 5. Access union fields

// RAW POINTERS
fn raw_pointers() {
    let mut num = 5;

    // Creating raw pointers is safe
    let r1 = &num as *const i32;
    let r2 = &mut num as *mut i32;

    // Dereferencing requires unsafe
    unsafe {
        println!("r1 is: {}", *r1);
        *r2 = 10;
        println!("r2 is: {}", *r2);
    }
}

// UNSAFE FUNCTION
// The function guarantees safety IF preconditions are met
unsafe fn dangerous() {
    // Code that assumes the caller verified invariants
}

fn call_dangerous() {
    // Must be in an unsafe block
    unsafe {
        dangerous();
    }
}

// SAFE ABSTRACTION over unsafe code
fn split_at_mut(values: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) {
    let len = values.len();
    let ptr = values.as_mut_ptr();

    assert!(mid <= len);  // Runtime check

    unsafe {
        // We know the two slices don't overlap
        (
            std::slice::from_raw_parts_mut(ptr, mid),
            std::slice::from_raw_parts_mut(ptr.add(mid), len - mid),
        )
    }
}

// FFI: calling C code
extern "C" {
    fn abs(input: i32) -> i32;
}

fn call_c_function() {
    unsafe {
        println!("Absolute value: {}", abs(-3));
    }
}

// Export a function for C
#[no_mangle]
pub extern "C" fn call_from_c() {
    println!("Called from C!");
}

// MUTABLE STATIC
static mut COUNTER: u32 = 0;

fn increment_counter() {
    unsafe {
        COUNTER += 1;
        println!("COUNTER: {}", COUNTER);
    }
}

// UNSAFE TRAIT
unsafe trait Dangerous {
    // Implementers guarantee invariants
}

unsafe impl Dangerous for i32 {
    // Implementer asserts respecting the trait's invariants
}

// Practical example: structure with internal pointer
pub struct MyVec<T> {
    ptr: *mut T,
    len: usize,
    capacity: usize,
}

impl<T> MyVec<T> {
    pub fn new() -> Self {
        Self {
            ptr: std::ptr::null_mut(),
            len: 0,
            capacity: 0,
        }
    }

    pub fn push(&mut self, value: T) {
        if self.len == self.capacity {
            self.grow();
        }

        unsafe {
            std::ptr::write(self.ptr.add(self.len), value);
        }
        self.len += 1;
    }

    fn grow(&mut self) {
        // Unsafe allocation/reallocation...
    }
}

impl<T> Drop for MyVec<T> {
    fn drop(&mut self) {
        unsafe {
            // Properly free memory
            for i in 0..self.len {
                std::ptr::drop_in_place(self.ptr.add(i));
            }
            if self.capacity > 0 {
                let layout = std::alloc::Layout::array::<T>(self.capacity).unwrap();
                std::alloc::dealloc(self.ptr as *mut u8, layout);
            }
        }
    }
}
Quy tac vang cua unsafe

Giam thieu pham vi code unsafe. Boc code unsafe trong cac abstraction an toan dam bao cac invariant. Code unsafe khong duoc lam hong bo nho safe xung quanh.

Cau hoi 15: Borrow checker hoat dong nhu the nao?

Borrow checker la cot loi cua compiler Rust, xac minh cac quy tac ownership va borrowing.

borrow_checker.rsrust
// Understanding how the borrow checker works

fn borrow_checker_basics() {
    let mut v = vec![1, 2, 3];

    // RULE 1: Either multiple immutable references or one mutable
    let r1 = &v;
    let r2 = &v;
    println!("{:?} {:?}", r1, r2);  // OK: multiple immutable references

    // From here, r1 and r2 are no longer used (NLL)

    let r3 = &mut v;  // OK thanks to Non-Lexical Lifetimes
    r3.push(4);
}

// The borrow checker tracks lifetimes
fn lifetime_tracking() {
    let mut data = String::from("hello");

    let slice = &data[..];  // Immutable borrow starts
    // data.push_str(" world");  // ERROR: cannot mutate during borrow
    println!("{}", slice);  // Last use of slice

    data.push_str(" world");  // OK: borrow ended
}

// Common problems and solutions
mod common_patterns {
    // Problem: borrowing two mutable fields
    struct Data {
        field1: Vec<i32>,
        field2: Vec<i32>,
    }

    fn problem(data: &mut Data) {
        // This sometimes doesn't compile directly:
        // let f1 = &mut data.field1;
        // let f2 = &mut data.field2;

        // Solution: destructuring
        let Data { field1, field2 } = data;
        field1.push(1);
        field2.push(2);
    }

    // Problem: iterate and modify
    fn iterate_and_modify() {
        let mut v = vec![1, 2, 3, 4, 5];

        // Does not compile:
        // for &x in &v {
        //     if x % 2 == 0 {
        //         v.push(x * 2);  // ERROR: borrowed by iterator
        //     }
        // }

        // Solution 1: collect indices first
        let to_add: Vec<i32> = v.iter()
            .filter(|&&x| x % 2 == 0)
            .map(|&x| x * 2)
            .collect();
        v.extend(to_add);

        // Solution 2: use explicit indices
        let len = v.len();
        for i in 0..len {
            if v[i] % 2 == 0 {
                let new_val = v[i] * 2;
                v.push(new_val);
            }
        }
    }

    // Problem: self-referential struct
    // struct SelfRef {
    //     data: String,
    //     slice: &str,  // Reference to data - IMPOSSIBLE
    // }

    // Solution: use indices or crates like ouroboros
    struct SafeSelfRef {
        data: String,
        slice_start: usize,
        slice_end: usize,
    }

    impl SafeSelfRef {
        fn get_slice(&self) -> &str {
            &self.data[self.slice_start..self.slice_end]
        }
    }
}

// Patterns to work around limitations
mod workarounds {
    use std::cell::RefCell;

    // Interior mutability when borrow checker is too restrictive
    struct Graph {
        nodes: RefCell<Vec<Node>>,
    }

    struct Node {
        value: i32,
    }

    impl Graph {
        fn add_node(&self, value: i32) {
            // Mutation possible despite &self
            self.nodes.borrow_mut().push(Node { value });
        }

        fn get_node(&self, index: usize) -> Option<i32> {
            self.nodes.borrow().get(index).map(|n| n.value)
        }
    }
}

Borrow checker co the cam thay han che luc dau, nhung cac rang buoc nay loai bo toan bo danh muc loi ton tai o cac ngon ngu khac.

Ket luan

Phong van Rust danh gia su hieu biet sau ve he thong ownership, dam bao an toan bo nho, va kha nang viet code concurrent khong co data race. Viec thanh thao cac khai niem nay tao su khac biet cho cac developer co the khai thac loi the doc dao cua Rust.

Danh sach chuan bi

  • Hieu ownership, borrowing va ba quy tac co ban
  • Biet khi nao va lam sao annotation lifetime
  • Thanh thao trait va su khac biet giua static va dynamic dispatch
  • Xu ly loi idiomatic voi Result va Option
  • Chon smart pointer phu hop theo ngu canh
  • Viet code concurrent voi Arc, Mutex va channel
  • Hieu async/await va cac runtime nhu tokio
  • Biet khi nao va lam sao su dung unsafe mot cach an toan

Bắt đầu luyện tập!

Kiểm tra kiến thức với mô phỏng phỏng vấn và bài kiểm tra kỹ thuật.

Chuan bi phong van Rust doi hoi luyen tap voi cac khai niem ownership doc dao. Cac bai tap tren Exercism, du an ca nhan va dong gop cho he sinh thai Rust se cung co kien thuc nay cho cac buoi phong van ky thuat doi hoi khat khe nhat.

Thẻ

#rust
#interview
#systems programming
#ownership
#technical interview

Chia sẻ

Bài viết liên quan