Rust Code Drills
This chapter is the practical companion to the oral questions. The goal is not to read these once. The goal is to retype them from memory until the patterns become automatic.
Ownership and Borrowing
Move
fn main() { let s1 = String::from("hello"); let s2 = s1; println!("{s2}"); }
Shared borrow
fn len_of(s: &String) -> usize { s.len() } fn main() { let s = String::from("hello"); let n = len_of(&s); println!("{s} {n}"); }
Mutable borrow
fn append_world(s: &mut String) { s.push_str(" world"); } fn main() { let mut s = String::from("hello"); append_world(&mut s); println!("{s}"); }
Shared reads or one writer
fn main() { let mut s = String::from("hello"); let r1 = &s; let r2 = &s; println!("{r1} {r2}"); let r3 = &mut s; r3.push('!'); println!("{r3}"); }
Ownership move into function
fn consume(s: String) { println!("{s}"); } fn main() { let s = String::from("hello"); consume(s); }
Types, Values, and Strings
String vs &str
fn greet(name: &str) { println!("hello, {name}"); } fn main() { let owned = String::from("bobby"); let borrowed: &str = &owned; greet(&owned); greet(borrowed); greet("rust"); }
Shadowing
fn main() { let x = 5; let x = x + 1; let x = x.to_string(); println!("{x}"); }
Tuple type
fn main() { let pair: (i32, i32) = (19, 34); println!("{} {}", pair.0, pair.1); }
Structs, Enums, and Pattern Matching
Struct and method
#![allow(unused)] fn main() { struct User { name: String, active: bool, } impl User { fn new(name: impl Into<String>) -> Self { Self { name: name.into(), active: true, } } fn deactivate(&mut self) { self.active = false; } } }
Enum and match
#![allow(unused)] fn main() { enum State { Ready, Running(u32), Failed(String), } fn describe(state: State) -> String { match state { State::Ready => "ready".to_string(), State::Running(pid) => format!("running: {pid}"), State::Failed(msg) => format!("failed: {msg}"), } } }
Option
fn first_char(s: &str) -> Option<char> { s.chars().next() } fn main() { match first_char("rust") { Some(c) => println!("{c}"), None => println!("empty"), } }
Result and ?
#![allow(unused)] fn main() { use std::num::ParseIntError; fn parse_port(input: &str) -> Result<u16, ParseIntError> { let port = input.parse::<u16>()?; Ok(port) } }
if let
fn main() { let maybe = Some(10); if let Some(v) = maybe { println!("{v}"); } }
Pattern binding in match
fn main() { let maybe = Some(42); match maybe { Some(x) => println!("value = {x}"), None => println!("nothing"), } }
Exhaustive match
fn main() { let x = 24; match x { 23 => println!("twenty-three"), 24 => println!("twenty-four"), _ => println!("something else"), } }
Struct pattern
struct User { name: String, active: bool, admin: bool, } fn main() { let user = User { name: "Bobby".to_string(), active: true, admin: false, }; match user { User { admin: true, .. } => println!("admin"), User { active: true, .. } => println!("active"), User { .. } => println!("some user"), } }
Traits and Generics
Trait implementation
#![allow(unused)] fn main() { trait Describe { fn describe(&self) -> String; } struct Job { id: u64, } impl Describe for Job { fn describe(&self) -> String { format!("job: {}", self.id) } } }
Generic with trait bound
#![allow(unused)] fn main() { fn print_twice<T: std::fmt::Display>(value: T) { println!("{value}"); println!("{value}"); } }
impl Trait
#![allow(unused)] fn main() { fn make_iter() -> impl Iterator<Item = i32> { vec![1, 2, 3].into_iter() } }
dyn Trait
#![allow(unused)] fn main() { fn show(x: &dyn std::fmt::Display) { println!("{x}"); } }
Lifetime example
#![allow(unused)] fn main() { fn longest<'a>(a: &'a str, b: &'a str) -> &'a str { if a.len() >= b.len() { a } else { b } } }
Collections and Iterators
Vec
fn main() { let mut nums = vec![1, 2, 3]; nums.push(4); for n in &nums { println!("{n}"); } }
HashMap
use std::collections::HashMap; fn main() { let mut counts = HashMap::new(); counts.insert("rust", 1); counts.entry("tokio").or_insert(1); }
Iterator pipeline
fn main() { let nums = vec![1, 2, 3, 4, 5, 6]; let evens: Vec<i32> = nums .into_iter() .filter(|n| n % 2 == 0) .map(|n| n * 10) .collect(); println!("{evens:?}"); }
Concurrency
Channel
use std::sync::mpsc; use std::thread; fn main() { let (tx, rx) = mpsc::channel(); thread::spawn(move || { tx.send(String::from("hello")).unwrap(); }); println!("{}", rx.recv().unwrap()); }
Arc<Mutex<T>>
use std::sync::{Arc, Mutex}; use std::thread; fn main() { let count = Arc::new(Mutex::new(0)); let mut handles = Vec::new(); for _ in 0..4 { let count = Arc::clone(&count); handles.push(thread::spawn(move || { let mut guard = count.lock().unwrap(); *guard += 1; })); } for handle in handles { handle.join().unwrap(); } println!("{}", *count.lock().unwrap()); }
Async
Basic async function
#![allow(unused)] fn main() { async fn fetch_value() -> u32 { 42 } }
tokio::main
#[tokio::main] async fn main() { let value = fetch_value().await; println!("{value}"); }
tokio::spawn
#[tokio::main] async fn main() { let handle = tokio::spawn(async { 5 + 5 }); let result = handle.await.unwrap(); println!("{result}"); }
tokio::select!
use tokio::time::{sleep, Duration}; #[tokio::main] async fn main() { tokio::select! { _ = sleep(Duration::from_millis(10)) => println!("timer 1"), _ = sleep(Duration::from_millis(20)) => println!("timer 2"), } }
Blocking hazard
#![allow(unused)] fn main() { async fn bad() { std::thread::sleep(std::time::Duration::from_millis(50)); } }
spawn_blocking
#[tokio::main] async fn main() { let result = tokio::task::spawn_blocking(|| { std::thread::sleep(std::time::Duration::from_millis(50)); 42 }) .await .unwrap(); println!("{result}"); }
Unsafe
Raw pointers
fn main() { let mut x = 5; let p1 = &x as *const i32; let p2 = &mut x as *mut i32; unsafe { println!("{}", *p1); *p2 = 10; } }
Safe wrapper around unsafe
#![allow(unused)] fn main() { fn first_byte(slice: &[u8]) -> Option<u8> { if slice.is_empty() { None } else { unsafe { Some(*slice.as_ptr()) } } } }
FFI
unsafe extern "C" { fn abs(input: i32) -> i32; } fn main() { unsafe { println!("{}", abs(-3)); } }
Drill Method
For each snippet:
- Read it once.
- Hide it.
- Re-type it from memory.
- Explain what ownership, borrowing, matching, or trait rule it demonstrates.
- Repeat until you can write it without hesitation.