Rustownership.rs
Ownership, borrowing, and clone
fn main() {
// Move: String assignment transfers ownership
let s1 = String::from("hello");
let s2 = s1; // s1 is MOVED — no longer valid
// println!("{}", s1); // ERROR: value borrowed after move
println!("{}", s2); // "hello"
// Clone: explicit deep copy — both are valid
let s3 = s2.clone();
println!("{} {}", s2, s3); // "hello hello"
// Copy types (primitives) are copied silently
let x = 42;
let y = x; // x is copied, not moved
println!("{} {}", x, y); // both valid
// Borrowing: reference without taking ownership
let s4 = String::from("world");
let len = calculate_length(&s4); // pass reference
println!("{} has length {}", s4, len); // s4 still valid
// Mutable borrow
let mut s5 = String::from("hello");
change(&mut s5);
println!("{}", s5); // "hello, world"
}
fn calculate_length(s: &String) -> usize {
s.len() // borrows but doesn't take ownership
}
fn change(s: &mut String) {
s.push_str(", world");
}Rusterror_handling.rs
Error handling with ? operator and thiserror
use std::fs;
use std::num::ParseIntError;
// Custom error with thiserror (add to Cargo.toml: thiserror = "1")
// #[derive(Debug, thiserror::Error)]
// enum AppError {
// #[error("IO error: {0}")]
// Io(#[from] std::io::Error),
// #[error("Parse error: {0}")]
// Parse(#[from] ParseIntError),
// }
// Manual custom error
#[derive(Debug)]
enum AppError {
Io(std::io::Error),
Parse(ParseIntError),
}
impl std::fmt::Display for AppError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
AppError::Io(e) => write!(f, "IO: {}", e),
AppError::Parse(e) => write!(f, "Parse: {}", e),
}
}
}
impl From<std::io::Error> for AppError { fn from(e: std::io::Error) -> Self { AppError::Io(e) } }
impl From<ParseIntError> for AppError { fn from(e: ParseIntError) -> Self { AppError::Parse(e) } }
// ? operator: return Err(e.into()) on failure
fn read_number_from_file(path: &str) -> Result<i32, AppError> {
let content = fs::read_to_string(path)?; // ? converts io::Error via From
let num = content.trim().parse::<i32>()?; // ? converts ParseIntError
Ok(num)
}
fn main() {
match read_number_from_file("number.txt") {
Ok(n) => println!("Number: {}", n),
Err(e) => eprintln!("Error: {}", e),
}
}Rustiterators.rs
Iterator chains: map, filter, collect
fn main() {
let nums = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// Lazy chain: nothing computed until collect()
let result: Vec<i32> = nums.iter()
.filter(|&&x| x % 2 == 0) // keep evens
.map(|&x| x * x) // square
.collect();
println!("{:?}", result); // [4, 16, 36, 64, 100]
// Sum, product, count
let sum: i32 = nums.iter().sum();
let evens = nums.iter().filter(|&&x| x % 2 == 0).count();
println!("sum={} evens={}", sum, evens); // sum=55 evens=5
// flat_map: flatten one level
let words = vec!["hello world", "foo bar"];
let all_words: Vec<&str> = words.iter()
.flat_map(|s| s.split_whitespace())
.collect();
println!("{:?}", all_words); // ["hello", "world", "foo", "bar"]
// zip: combine two iterators
let names = vec!["Alice", "Bob", "Carol"];
let scores = vec![95, 87, 92];
let paired: Vec<_> = names.iter().zip(scores.iter()).collect();
println!("{:?}", paired); // [("Alice", 95), ("Bob", 87), ("Carol", 92)]
// take_while, skip_while
let until_big: Vec<_> = nums.iter().take_while(|&&x| x < 5).collect();
// [1, 2, 3, 4]
// chain: concatenate iterators
let a = vec![1, 2, 3];
let b = vec![4, 5, 6];
let combined: Vec<_> = a.iter().chain(b.iter()).collect();
}Rustenums_match.rs
Enums, pattern matching, and if let
// Enum with associated data — like a tagged union
#[derive(Debug)]
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(u8, u8, u8),
}
fn process(msg: Message) -> String {
match msg {
Message::Quit => String::from("Quit"),
Message::Move { x, y } => format!("Move to ({}, {})", x, y),
Message::Write(text) => format!("Write: {}", text),
Message::ChangeColor(r,g,b) => format!("Color: rgb({},{},{})", r, g, b),
// match is exhaustive — compiler requires all variants
}
}
fn main() {
println!("{}", process(Message::Move { x: 10, y: 20 }));
println!("{}", process(Message::Write(String::from("hello"))));
// Option: Some(T) or None — Rust's null-safe type
let maybe: Option<i32> = Some(42);
// if let: extract if matches — avoids full match
if let Some(n) = maybe {
println!("Got: {}", n);
}
// while let
let mut stack = vec![1, 2, 3];
while let Some(top) = stack.pop() {
println!("{}", top); // 3, 2, 1
}
// Match guard: extra condition
let n = 7;
let desc = match n {
x if x < 0 => "negative",
0 => "zero",
x if x % 2 == 0 => "positive even",
_ => "positive odd",
};
println!("{}", desc); // positive odd
// Binding with @
let msg = match n {
x @ 1..=10 => format!("1-10: got {}", x),
_ => String::from("other"),
};
}Rusttraits.rs
Traits, default implementations, and trait objects
use std::fmt;
trait Animal {
fn name(&self) -> &str;
fn speak(&self) -> String;
// Default implementation
fn describe(&self) -> String {
format!("{} says: {}", self.name(), self.speak())
}
}
struct Dog { name: String }
struct Cat { name: String }
impl Animal for Dog {
fn name(&self) -> &str { &self.name }
fn speak(&self) -> String { String::from("Woof!") }
}
impl Animal for Cat {
fn name(&self) -> &str { &self.name }
fn speak(&self) -> String { String::from("Meow!") }
fn describe(&self) -> String {
format!("{} silently judges you", self.name()) // override default
}
}
// Generic: T must implement Animal — static dispatch (monomorphised)
fn print_animal<T: Animal>(a: &T) { println!("{}", a.describe()); }
// Trait object: dynamic dispatch — heterogeneous collection
fn loudest(animals: &[Box<dyn Animal>]) -> &dyn Animal {
animals.iter()
.max_by_key(|a| a.speak().len())
.map(|a| a.as_ref())
.unwrap()
}
fn main() {
let animals: Vec<Box<dyn Animal>> = vec![
Box::new(Dog { name: String::from("Rex") }),
Box::new(Cat { name: String::from("Whiskers") }),
];
for a in &animals { println!("{}", a.describe()); }
}Rustclosures.rs
Closures: Fn, FnMut, FnOnce
fn apply<F: Fn(i32) -> i32>(f: F, x: i32) -> i32 { f(x) }
fn apply_mut<F: FnMut() -> i32>(mut f: F) -> i32 { f() }
fn apply_once<F: FnOnce() -> String>(f: F) -> String { f() }
fn make_adder(n: i32) -> impl Fn(i32) -> i32 {
move |x| x + n // move: capture n by value into the closure
}
fn main() {
let double = |x| x * 2;
println!("{}", apply(double, 5)); // 10
println!("{}", apply(|x| x + 1, 10)); // 11
// FnMut: closure that mutates captured state
let mut count = 0;
let mut counter = || { count += 1; count };
println!("{}", apply_mut(&mut counter)); // 1
println!("{}", apply_mut(&mut counter)); // 2
// FnOnce: consumes captured values — can only be called once
let name = String::from("Alice");
let greet = move || format!("Hello, {}!", name); // name moved into closure
println!("{}", apply_once(greet)); // "Hello, Alice!"
// println!("{}", name); // ERROR: name was moved
// Returning closures
let add5 = make_adder(5);
let add10 = make_adder(10);
println!("{} {}", add5(3), add10(3)); // 8, 13
// Closure as iterator adapter
let threshold = 3;
let filtered: Vec<_> = (1..=10)
.filter(|&x| x > threshold) // captures threshold
.collect();
}Ruststructs_impl.rs
Structs, impl blocks, and derive macros
#[derive(Debug, Clone, PartialEq)] // auto-implement common traits
struct Rectangle {
width: f64,
height: f64,
}
impl Rectangle {
// Associated function (static method) — constructor
fn new(width: f64, height: f64) -> Self {
assert!(width > 0.0 && height > 0.0, "dimensions must be positive");
Rectangle { width, height }
}
fn square(size: f64) -> Self { Rectangle::new(size, size) }
// Method: takes &self (immutable borrow)
fn area(&self) -> f64 { self.width * self.height }
fn perimeter(&self) -> f64 { 2.0 * (self.width + self.height) }
fn is_square(&self) -> bool { self.width == self.height }
// Mutable method: takes &mut self
fn scale(&mut self, factor: f64) {
self.width *= factor;
self.height *= factor;
}
}
// Implement Display trait
impl std::fmt::Display for Rectangle {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}x{}", self.width, self.height)
}
}
fn main() {
let mut r = Rectangle::new(4.0, 6.0);
println!("area: {}", r.area()); // 24
println!("rect: {}", r); // 4x6 (Display)
println!("debug: {:?}", r); // Rectangle { width: 4.0, height: 6.0 }
r.scale(2.0);
println!("scaled: {}", r); // 8x12
let sq = Rectangle::square(5.0);
println!("is_square: {}", sq.is_square()); // true
}Rustsmart_pointers.rs
Box, Rc, Arc, and interior mutability
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use std::cell::RefCell;
fn main() {
// Box<T>: heap allocation — use for recursive types or large values
let boxed = Box::new(42);
println!("{}", *boxed); // 42
// Recursive type: without Box, size would be infinite
#[derive(Debug)]
enum List { Cons(i32, Box<List>), Nil }
let list = List::Cons(1, Box::new(List::Cons(2, Box::new(List::Nil))));
// Rc<T>: shared ownership (single-threaded)
let a = Rc::new(String::from("shared"));
let b = Rc::clone(&a); // increment ref count — no copy
let c = Rc::clone(&a);
println!("count: {}", Rc::strong_count(&a)); // 3
// a dropped — count decremented; freed when count reaches 0
// RefCell<T>: interior mutability — runtime borrow checking
let shared = Rc::new(RefCell::new(vec![1, 2, 3]));
let shared2 = Rc::clone(&shared);
shared.borrow_mut().push(4); // mutable borrow checked at runtime
println!("{:?}", shared2.borrow()); // [1, 2, 3, 4]
// Arc<T>: shared ownership across threads (atomic ref count)
let arc = Arc::new(Mutex::new(0));
let arc2 = Arc::clone(&arc);
std::thread::spawn(move || {
let mut n = arc2.lock().unwrap();
*n += 1;
}).join().unwrap();
println!("{}", *arc.lock().unwrap()); // 1
}Rustgenerics_traits.rs
Generics with trait bounds and where clauses
use std::fmt::Display;
// Multiple trait bounds: T must be Display and PartialOrd
fn largest<T: PartialOrd + Display>(list: &[T]) -> &T {
let mut biggest = &list[0];
for item in list {
if item > biggest { biggest = item; }
}
biggest
}
// where clause: cleaner for complex bounds
fn print_pair<T, U>(a: T, b: U)
where
T: Display + Clone,
U: Display + std::fmt::Debug,
{
println!("({}, {:?})", a, b);
}
// Generic struct with trait bound
struct Wrapper<T: Display> {
value: T,
}
impl<T: Display> Wrapper<T> {
fn show(&self) { println!("value: {}", self.value); }
}
// impl Trait: shorthand for generic in function signatures
fn make_greeting(name: impl Display) -> String {
format!("Hello, {}!", name)
}
fn apply<T, F: Fn(T) -> T>(val: T, f: F) -> T { f(val) }
fn main() {
let numbers = vec![34, 50, 25, 100, 65];
println!("largest: {}", largest(&numbers)); // 100
let words = vec!["apple", "fig", "banana"];
println!("largest: {}", largest(&words)); // fig
let w = Wrapper { value: 42 };
w.show(); // value: 42
println!("{}", make_greeting("Alice"));
println!("{}", apply(5, |x| x * x)); // 25
}Rustcollections.rs
HashMap, Vec patterns, and entry API
use std::collections::HashMap;
fn main() {
// Vec: dynamic array
let mut v: Vec<i32> = Vec::new();
v.push(1); v.push(2); v.push(3);
// Iterate
for n in &v { print!("{} ", n); }
println!();
// Slices
let slice = &v[1..]; // [2, 3]
println!("{:?}", slice);
// HashMap
let mut scores: HashMap<String, i32> = HashMap::new();
scores.insert(String::from("Alice"), 95);
scores.insert(String::from("Bob"), 87);
// entry API: insert only if missing
scores.entry(String::from("Carol")).or_insert(90);
scores.entry(String::from("Alice")).or_insert(0); // not updated — Alice exists
println!("{:?}", scores.get("Alice")); // Some(95)
// entry + modify
let text = "hello world wonderful world";
let mut word_count: HashMap<&str, u32> = HashMap::new();
for word in text.split_whitespace() {
let count = word_count.entry(word).or_insert(0);
*count += 1; // dereference to modify
}
println!("{:?}", word_count); // {hello:1, world:2, wonderful:1}
// Collecting into HashMap
let pairs = vec![("a", 1), ("b", 2), ("c", 3)];
let map: HashMap<&str, i32> = pairs.into_iter().collect();
}Rustlifetimes.rs
Lifetime annotations
// Lifetime annotation: 'a says the return ref lives at least as long as both inputs
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
// Struct with a reference field — must annotate lifetime
struct Important<'a> {
content: &'a str, // content must outlive Important
}
impl<'a> Important<'a> {
fn announce(&self) -> &str {
self.content // lifetime elision: returns 'a implicitly
}
}
// 'static: lives for the entire program duration
// String literals are 'static
fn get_greeting() -> &'static str { "Hello!" }
fn main() {
let s1 = String::from("long string");
let result;
{
let s2 = String::from("xyz");
result = longest(s1.as_str(), s2.as_str());
println!("longest: {}", result); // OK — s2 still alive
}
// println!("{}", result); // ERROR — s2 dropped, result would dangle
let novel = String::from("Call me Ishmael. Some years ago...");
let first_sentence = novel.split('.').next().expect("Could not find a '.'");
let imp = Important { content: first_sentence };
println!("{}", imp.announce());
}Rustasync_rust.rs
Async/await with Tokio
// Cargo.toml: tokio = { version = "1", features = ["full"] }
use tokio::time::{sleep, Duration};
async fn fetch_data(id: u32) -> String {
sleep(Duration::from_millis(100)).await; // non-blocking sleep
format!("data-{}", id)
}
async fn sequential() {
// Awaited in sequence — total ~200ms
let a = fetch_data(1).await;
let b = fetch_data(2).await;
println!("sequential: {} {}", a, b);
}
async fn concurrent() {
// join!: run concurrently — total ~100ms (parallel)
let (a, b) = tokio::join!(
fetch_data(1),
fetch_data(2),
);
println!("concurrent: {} {}", a, b);
}
// Spawn: run a task on the runtime
async fn with_spawn() {
let handle = tokio::spawn(async {
fetch_data(99).await
});
let result = handle.await.unwrap(); // join the task
println!("spawned: {}", result);
}
#[tokio::main]
async fn main() {
sequential().await;
concurrent().await;
with_spawn().await;
}Rustserde_json.rs
Serialisation with serde
// Cargo.toml:
// serde = { version = "1", features = ["derive"] }
// serde_json = "1"
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
struct User {
id: u32,
name: String,
email: String,
#[serde(skip_serializing_if = "Option::is_none")] // omit if None
age: Option<u32>,
#[serde(rename = "is_active")] // rename field in JSON
active: bool,
}
fn main() -> Result<(), serde_json::Error> {
let user = User {
id: 1,
name: String::from("Alice"),
email: String::from("alice@example.com"),
age: Some(30),
active: true,
};
// Serialize to JSON string
let json = serde_json::to_string(&user)?;
println!("{}", json);
// {"id":1,"name":"Alice","email":"alice@example.com","age":30,"is_active":true}
// Pretty print
let pretty = serde_json::to_string_pretty(&user)?;
println!("{}", pretty);
// Deserialize from JSON
let raw = r#"{"id":2,"name":"Bob","email":"bob@b.com","is_active":false}"#;
let user2: User = serde_json::from_str(raw)?;
println!("{:?}", user2); // age is None (absent in JSON)
// Deserialize to Value (unknown schema)
let v: serde_json::Value = serde_json::from_str(raw)?;
println!("{}", v["name"]); // "Bob"
Ok(())
}Rust reference — Rust overview · Learn Rust
Everything Rust in one place — learning paths, reference, playground, and more.
Rust Hub →