***1. Create a Rust program that reads data from a file and counts occurrences of a word.
This program:
- Reads a file
- Counts occurrences of a target word
- Demonstrates file handling and string processing
Rust Program
use std::fs;
fn main() {
let filename = "data.txt";
let target_word = "rust";
let content = fs::read_to_string(filename)
.expect("Failed to read file");
let count = content
.split_whitespace()
.filter(|word| word.to_lowercase() == target_word)
.count();
println!("Occurrences of '{}': {}", target_word, count);
}
Example Input File
Rust is fast.
Rust is memory safe.
I love Rust.
Output
Occurrences of 'rust': 3
Important Concepts
| Concept | Usage |
|---|---|
fs::read_to_string | Read file |
split_whitespace | Tokenize words |
filter | Match target word |
count | Count matches |
Interview Tip
Mention:
- Error handling
- Case-insensitive comparison
- Efficient string processing
***2. Reverse a string without using built-in reverse methods.
This question tests:
- String iteration
- Character handling
- Ownership concepts
Rust Program
fn reverse_string(input: &str) -> String {
let mut reversed = String::new();
for ch in input.chars() {
reversed = format!("{}{}", ch, reversed);
}
reversed
}
fn main() {
let text = "Rust";
let result = reverse_string(text);
println!("Reversed: {}", result);
}
Output
Reversed: tsuR
Important Concepts
| Concept | Usage |
|---|---|
chars() | Iterate characters |
String | Dynamic string building |
| UTF-8 safety | Proper char handling |
Time Complexity
O(n²)
because repeated string concatenation reallocates memory.
Optimized Version
fn reverse_string(input: &str) -> String {
let mut chars: Vec<char> = input.chars().collect();
let mut left = 0;
let mut right = chars.len() - 1;
while left < right {
chars.swap(left, right);
left += 1;
right -= 1;
}
chars.into_iter().collect()
}
Interview Tip
Discuss:
- UTF-8 correctness
- Complexity optimization
- Manual algorithm implementation
***3. Implement factorial using recursion and iteration.
This question tests:
- Recursion
- Loops
- Base cases
- Performance understanding
Recursive Factorial
fn factorial_recursive(n: u64) -> u64 {
if n == 0 || n == 1 {
1
} else {
n * factorial_recursive(n - 1)
}
}
Iterative Factorial
fn factorial_iterative(n: u64) -> u64 {
let mut result = 1;
for i in 2..=n {
result *= i;
}
result
}
Main Function
fn main() {
let num = 5;
println!("Recursive: {}", factorial_recursive(num));
println!("Iterative: {}", factorial_iterative(num));
}
Output
Recursive: 120
Iterative: 120
Recursion vs Iteration
| Feature | Recursion | Iteration |
|---|---|---|
| Readability | Simpler | Efficient |
| Stack Usage | Higher | Lower |
| Performance | Slightly slower | Faster |
Interview Tip
Mention:
- Stack overflow risk
- Tail recursion limitations
- Iterative optimization
***4. Find duplicates in a vector.
This problem tests:
- Hashing
- Collections
- Time complexity optimization
Rust Program
use std::collections::HashSet;
fn find_duplicates(nums: Vec<i32>) {
let mut seen = HashSet::new();
let mut duplicates = HashSet::new();
for num in nums {
if !seen.insert(num) {
duplicates.insert(num);
}
}
println!("Duplicates: {:?}", duplicates);
}
fn main() {
let numbers = vec![1, 2, 3, 2, 4, 5, 1];
find_duplicates(numbers);
}
Output
Duplicates: {1, 2}
Time Complexity
| Operation | Complexity |
|---|---|
| Insert/Search | O(1) average |
| Overall | O(n) |
Why HashSet?
HashSet provides:
- Fast lookup
- Unique storage
- Efficient duplicate detection
Interview Tip
Mention:
- Time complexity
- Space trade-offs
- Alternative sorting solution
***5. Create a simple HTTP server in Rust.
This project demonstrates:
- Networking
- TCP handling
- HTTP basics
Rust Program
use std::io::{Read, Write};
use std::net::TcpListener;
fn main() {
let listener = TcpListener::bind("127.0.0.1:8080")
.expect("Failed to bind");
println!("Server running on port 8080");
for stream in listener.incoming() {
let mut stream = stream.unwrap();
let mut buffer = [0; 1024];
stream.read(&mut buffer).unwrap();
let response =
"HTTP/1.1 200 OK\r\n\r\nHello from Rust!";
stream.write(response.as_bytes()).unwrap();
stream.flush().unwrap();
}
}
How It Works
| Component | Purpose |
|---|---|
TcpListener | Listen for connections |
incoming() | Accept clients |
stream.read() | Read request |
stream.write() | Send response |
Test in Browser
http://127.0.0.1:8080
Interview Tip
Discuss:
- Blocking I/O limitations
- Async alternatives
- HTTP parsing improvements
***6. Write a Rust networking program to send data between two machines.
This demonstrates:
- TCP communication
- Client-server networking
- Socket programming
Server Code
use std::io::Read;
use std::net::TcpListener;
fn main() {
let listener = TcpListener::bind("0.0.0.0:8080").unwrap();
println!("Server listening...");
for stream in listener.incoming() {
let mut stream = stream.unwrap();
let mut buffer = [0; 1024];
let bytes = stream.read(&mut buffer).unwrap();
println!(
"Received: {}",
String::from_utf8_lossy(&buffer[..bytes])
);
}
}
Client Code
use std::io::Write;
use std::net::TcpStream;
fn main() {
let mut stream =
TcpStream::connect("127.0.0.1:8080").unwrap();
stream.write_all(b"Hello from client").unwrap();
}
Networking Concepts
| Concept | Purpose |
|---|---|
| TCP | Reliable communication |
| Socket | Connection endpoint |
| Stream | Data transfer |
Interview Tip
Mention:
- Error handling
- Async networking
- Serialization formats
***7. Build a multithreaded web scraper in Rust.
This project demonstrates:
- Concurrency
- Networking
- Thread management
Cargo Dependencies
[dependencies]
reqwest = { version = "0.11", features = ["blocking"] }
Rust Program
use std::thread;
fn fetch(url: &str) {
let body = reqwest::blocking::get(url)
.unwrap()
.text()
.unwrap();
println!("Fetched {} bytes from {}", body.len(), url);
}
fn main() {
let urls = vec![
"https://example.com",
"https://rust-lang.org",
];
let mut handles = vec![];
for url in urls {
let url = url.to_string();
let handle = thread::spawn(move || {
fetch(&url);
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
}
Key Concepts
| Concept | Purpose |
|---|---|
thread::spawn | Parallel tasks |
move | Ownership transfer |
join() | Wait for completion |
Interview Tip
Discuss:
- Async alternative using Tokio
- Rate limiting
- Error retries
***8. Implement producer-consumer using channels.
This demonstrates:
- Thread communication
- Message passing concurrency
Rust Program
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
for i in 1..=5 {
tx.send(i).unwrap();
println!("Produced {}", i);
}
});
for received in rx {
println!("Consumed {}", received);
}
}
Output
Produced 1
Consumed 1
...
Important Concepts
| Concept | Purpose |
|---|---|
| Channel | Thread communication |
| Producer | Sends data |
| Consumer | Receives data |
Why Channels Matter
Channels:
- Avoid shared mutable state
- Simplify concurrency
- Improve safety
Interview Tip
Mention:
- Multiple producers
- Buffered channels
- Async channels
***9. Build a CLI todo app using Cargo.
This project demonstrates:
- CLI development
- File persistence
- Cargo usage
Cargo Command
cargo new todo_app
Rust Program
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 3 {
println!("Usage: todo add <task>");
return;
}
let command = &args[1];
let task = &args[2];
match command.as_str() {
"add" => {
println!("Task added: {}", task);
}
_ => {
println!("Unknown command");
}
}
}
Example Usage
cargo run add "Learn Rust"
Improvements
Possible enhancements:
- File storage
- JSON persistence
- Task deletion
- Colored output
Interview Tip
Discuss:
- CLI parsing libraries
- Persistent storage
- Error handling
***10. Build an async REST API using Tokio + Axum/Actix.
This demonstrates:
- Async Rust
- Web APIs
- Tokio runtime
Cargo Dependencies
[dependencies]
tokio = { version = "1", features = ["full"] }
axum = "0.7"
Rust Program
use axum::{routing::get, Router};
async fn hello() -> &'static str {
"Hello, Rust API!"
}
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/", get(hello));
let listener =
tokio::net::TcpListener::bind("0.0.0.0:3000")
.await
.unwrap();
axum::serve(listener, app)
.await
.unwrap();
}
Test API
http://localhost:3000
Key Concepts
| Concept | Purpose |
|---|---|
| Tokio | Async runtime |
| Axum | Web framework |
| Async handler | Non-blocking request handling |
Interview Tip
Discuss:
- Middleware
- Database integration
- Authentication
***11. Implement a thread pool from scratch.
This project demonstrates:
- Concurrency
- Worker threads
- Task scheduling
Core Components
| Component | Purpose |
|---|---|
| Worker threads | Execute tasks |
| Channel | Task queue |
| ThreadPool | Manage workers |
Simplified Thread Pool
use std::sync::{mpsc, Arc, Mutex};
use std::thread;
type Job = Box<dyn FnOnce() + Send + 'static>;
struct ThreadPool {
workers: Vec<thread::JoinHandle<()>>,
sender: mpsc::Sender<Job>,
}
impl ThreadPool {
fn new(size: usize) -> Self {
let (sender, receiver) = mpsc::channel();
let receiver = Arc::new(Mutex::new(receiver));
let mut workers = vec![];
for _ in 0..size {
let receiver = Arc::clone(&receiver);
let handle = thread::spawn(move || {
loop {
let job = receiver
.lock()
.unwrap()
.recv()
.unwrap();
job();
}
});
workers.push(handle);
}
Self { workers, sender }
}
}
Interview Tip
Discuss:
- Graceful shutdown
- Work stealing
- Async executors comparison
***12. Build a mini key-value store in Rust.
This project demonstrates:
- HashMap usage
- Persistence concepts
- CRUD operations
Rust Program
use std::collections::HashMap;
fn main() {
let mut store = HashMap::new();
store.insert("name", "Rust");
store.insert("type", "language");
println!("{:?}", store.get("name"));
}
Key Concepts
| Concept | Purpose |
|---|---|
HashMap | Key-value storage |
| Insert/Get | CRUD operations |
| Persistence | Future enhancement |
Possible Improvements
- File persistence
- Async networking
- WAL logging
- LSM tree
Interview Tip
Mention:
- Database internals
- Concurrency handling
- Disk persistence
***13. Create a custom smart pointer.
This demonstrates:
-
Dereftrait - Smart pointer internals
Rust Program
use std::ops::Deref;
struct MyBox<T>(T);
impl<T> MyBox<T> {
fn new(value: T) -> Self {
MyBox(value)
}
}
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
fn main() {
let x = MyBox::new(10);
println!("{}", *x);
}
Key Concepts
| Concept | Purpose |
|---|---|
Deref | Pointer behavior |
| Generic types | Reusability |
| Smart pointers | Ownership abstraction |
Interview Tip
Discuss:
-
Drop - Deref coercion
- Heap allocation
***14. Implement a lock-free queue.
This is an advanced concurrency/system design problem.
Lock-free structures use:
- Atomic operations
- Compare-and-swap (CAS)
Simplified Concept
Head -> Node -> Node
Threads modify pointers atomically.
Basic Atomic Example
use std::sync::atomic::{AtomicUsize, Ordering};
fn main() {
let counter = AtomicUsize::new(0);
counter.fetch_add(1, Ordering::SeqCst);
println!("{}", counter.load(Ordering::SeqCst));
}
Real Lock-Free Queue Requirements
A production implementation requires:
- Atomic linked list operations
- Memory reclamation
- ABA prevention
- Hazard pointers/epoch GC
Popular Library
Many Rust projects use:
- crossbeam
for lock-free data structures.
Interview Tip
For interviews:
- Explain atomic CAS operations
- Discuss ABA problem
- Mention memory ordering semantics