🦀 Systems Programming
Rust Complete Cheatsheet
Ownership, borrowing, structs, traits, Result/Option and Cargo — complete Rust reference.
01
Ownership & Borrowing
▼
RUSTOwnership — Rust's core concept
// Rule 1: Each value has one owner
// Rule 2: When owner goes out of scope, value is dropped
// Rule 3: Can't have two owners at once
let s1 = String::from("hello");
let s2 = s1; // s1 MOVED to s2 — s1 no longer valid!
// println!("{}", s1); // ERROR: value moved
let s3 = s2.clone(); // deep copy — both valid
println!("{} {}", s2, s3);
// Borrowing — references (&)
fn calculate_length(s: &String) -> usize {
s.len() // borrow, don't take ownership
}
let len = calculate_length(&s2); // pass reference
// Mutable reference
fn change(s: &mut String) { s.push_str(", world"); }
let mut s4 = String::from("hello");
change(&mut s4);
// RULES: only ONE mutable reference at a time
// OR multiple immutable references — never both together
💡
Rust's ownership prevents memory leaks and data races AT COMPILE TIME. No garbage collector needed.
02
Data Types
▼
RUSTTypes and variables
// Scalar types
let x: i32 = -42; // i8 i16 i32 i64 i128 isize
let y: u64 = 9999999; // u8 u16 u32 u64 u128 usize
let f: f64 = 3.14; // f32 f64
let b: bool = true;
let c: char = '🦀'; // Unicode scalar value
// Compound types
let tup: (i32, f64, u8) = (500, 6.4, 1);
let (a, b, c) = tup; // destructuring
let first = tup.0; // index access
let arr: [i32; 5] = [1, 2, 3, 4, 5]; // fixed size!
let arr2 = [3; 5]; // [3, 3, 3, 3, 3]
let slice = &arr[1..3]; // &[i32] slice
// String types
let s_lit: &str = "hello"; // string slice — stack
let s_own: String = String::from("world"); // owned — heap
let combined = format!("{} {}", s_lit, s_own);
// Shadowing
let spaces = " "; // &str
let spaces = spaces.len(); // usize — shadows previous!
03
Functions & Closures
▼
RUSTFunctions and closures
fn add(a: i32, b: i32) -> i32 { a + b } // implicit return (no semicolon)
fn largest(list: &[T]) -> &T {
let mut largest = &list[0];
for item in list { if item > largest { largest = item; } }
largest
}
// Closures — anonymous functions
let double = |x| x * 2;
let add_n = |n| move |x| x + n; // capture by move
let five_doubled = double(5); // 10
// Higher-order functions
let nums = vec![1, 2, 3, 4, 5];
let doubled: Vec = nums.iter().map(|&x| x * 2).collect();
let evens: Vec<&i32> = nums.iter().filter(|&&x| x % 2 == 0).collect();
let sum: i32 = nums.iter().sum();
04
Structs & Enums
▼
RUSTStructs and Enums
// Struct
#[derive(Debug, Clone)]
struct User {
name: String,
email: String,
age: u32,
active: bool,
}
impl User {
fn new(name: &str, email: &str) -> User {
User { name: String::from(name), email: String::from(email), age: 0, active: true }
}
fn greeting(&self) -> String { format!("Hi, I'm {}", self.name) }
fn make_inactive(&mut self) { self.active = false; }
}
// Enum (more powerful than C enums)
#[derive(Debug)]
enum Shape {
Circle(f64), // radius
Rectangle(f64, f64), // width, height
Triangle { base: f64, height: f64 }, // named fields
}
impl Shape {
fn area(&self) -> f64 {
match self {
Shape::Circle(r) => std::f64::consts::PI * r * r,
Shape::Rectangle(w, h) => w * h,
Shape::Triangle { base, height } => 0.5 * base * height,
}
}
}
05
Pattern Matching
▼
RUSTPattern matching
// match — exhaustive!
let x = 5;
match x {
1 => println!("one"),
2 | 3 => println!("two or three"),
4..=9 => println!("four to nine"),
_ => println!("other"),
}
// if let — for single pattern
let some_val: Option = Some(42);
if let Some(n) = some_val { println!("Got: {}", n); }
// while let
let mut stack = vec![1, 2, 3];
while let Some(top) = stack.pop() { println!("{}", top); }
// Destructuring in patterns
let (a, b, c) = (1, 2, 3);
let Point { x, y } = p;
let [first, .., last] = arr.as_slice();
// Guards
match x {
n if n < 0 => println!("negative"),
n if n % 2 == 0 => println!("positive even"),
_ => println!("positive odd"),
}
06
Error Handling
▼
RUSTError handling with Result and Option
use std::num::ParseIntError; // Option— value may be absent fn divide(a: f64, b: f64) -> Option { if b == 0.0 { None } else { Some(a / b) } } match divide(10.0, 2.0) { Some(result) => println!("Result: {}", result), None => println!("Cannot divide by zero"), } divide(10.0, 2.0).unwrap_or(0.0); // default on None divide(10.0, 2.0).unwrap(); // panic on None! // Result — value or error fn parse_num(s: &str) -> Result { s.parse:: () } match parse_num("42") { Ok(n) => println!("Parsed: {}", n), Err(e) => println!("Error: {}", e), } // ? operator — propagate errors fn read_number(s: &str) -> Result { let n = s.trim().parse:: ()?; // return early if Err Ok(n * 2) }
💡
Use ? operator to propagate errors. Avoid .unwrap() in production — it panics on None/Err.
07
Traits
▼
RUSTTraits — Rust's interfaces
// Define trait
trait Summary {
fn summarise(&self) -> String;
fn preview(&self) -> String { // default implementation
format!("Read more: {}", self.summarise())
}
}
// Implement trait
struct Article { title: String, content: String }
impl Summary for Article {
fn summarise(&self) -> String {
format!("{}: {}...", self.title, &self.content[..50])
}
}
// Trait bounds (generics with constraints)
fn notify(item: &impl Summary) { println!("{}", item.summarise()); }
fn notify(item: &T) { println!("{}", item.summarise()); }
fn notify(item: &T) {} // multiple bounds
// Common traits
// Display — fmt::Display (how to print)
// Debug — #[derive(Debug)] or manual
// Clone — #[derive(Clone)]
// Copy — types that copy by default (i32, f64, bool, char)
// Iterator — .next() -> Option
// From / Into — type conversions
08
Iterators & Closures
▼
RUSTIterators
let nums = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // Iterator adapters (lazy — don't run until consumed) nums.iter() // &i32 iterator nums.into_iter() // owned i32 iterator nums.iter_mut() // &mut i32 iterator // Chaining adapters let result: Vec= nums.iter() .filter(|&&x| x % 2 == 0) // keep even .map(|&x| x * x) // square each .take(3) // only first 3 .collect(); // [4, 16, 36] // Consuming adapters nums.iter().sum:: (); // 55 nums.iter().count(); // 10 nums.iter().max(); // Some(10) nums.iter().any(|&x| x > 9); // true nums.iter().all(|&x| x > 0); // true nums.iter().position(|&x| x == 5); // Some(4) // zip let a = vec![1,2,3]; let b = vec!['a','b','c']; let zipped: Vec<_> = a.iter().zip(b.iter()).collect(); // enumerate for (i, val) in nums.iter().enumerate() { println!("{}: {}", i, val); }
09
Cargo & Modules
▼
RUSTCargo and modules
# Cargo commands
cargo new myproject # create new project
cargo new --lib mylib # create library
cargo build # compile debug
cargo build --release # optimized release build
cargo run # build and run
cargo test # run tests
cargo doc --open # generate + open docs
cargo fmt # format code
cargo clippy # lint
cargo add serde # add dependency (modern)
# Cargo.toml
[package]
name = "myproject"
version = "0.1.0"
edition = "2021"
[dependencies]
serde = { version = "1", features = ["derive"] }
tokio = { version = "1", features = ["full"] } # async runtime
# Modules
mod utils {
pub fn greet(name: &str) { println!("Hello, {}!", name); }
pub mod math { pub fn add(a: i32, b: i32) -> i32 { a + b } }
}
use utils::greet;
use utils::math::add;
10
Mini Quizzes
▼
❓ Quiz 1
What happens when you assign one String to another variable in Rust?
Rust transfers ownership (moves) when assigning heap-allocated types like String. The original variable is invalidated. Use .clone() for a deep copy, or use references (&) to borrow without moving.
❓ Quiz 2
What does the ? operator do in a Result context?
The ? operator is shorthand for: if Err(e), return Err(e.into()). If Ok(v), continue with v. It propagates errors up the call stack cleanly. Can only be used in functions that return Result or Option.