Rust

Rust syntax, ownership, borrowing, and common patterns for safe systems programming.

languages
rustsystemsbackendmemory-safe

Variables & Data Types

// Variables (immutable by default)
let name = "Alice";           // &str (string slice)
let age: i32 = 30;            // i32
let height = 5.9_f64;         // f64
let is_active = true;         // bool
let letter = 'A';             // char

// Mutable variable
let mut count = 0;
count += 1;

// Constants (must have type annotation)
const MAX_POINTS: u32 = 100_000;

// Shadowing
let x = 5;
let x = x + 1;  // New variable, shadows previous

// Integer types: i8, i16, i32, i64, i128, isize
// Unsigned: u8, u16, u32, u64, u128, usize
// Float: f32, f64

Strings

// String types
let s1: &str = "hello";           // String slice (immutable)
let s2: String = String::from("hello"); // Owned String

// String methods
let s = String::from("hello world");
s.len()                    // Length in bytes
s.is_empty()               // Check if empty
s.contains("world")        // Check substring
s.replace("world", "rust") // Replace
s.to_uppercase()           // UPPERCASE
s.to_lowercase()           // lowercase
s.trim()                   // Remove whitespace
s.split(" ")               // Split iterator

// String concatenation
let s1 = String::from("Hello, ");
let s2 = String::from("world!");
let s3 = s1 + &s2;         // s1 is moved
let s4 = format!("{}{}", s3, "!"); // format! macro

// Converting
let num: i32 = "42".parse().unwrap();
let s = 42.to_string();

Vectors

// Creating vectors
let v1: Vec<i32> = Vec::new();
let v2 = vec![1, 2, 3, 4, 5];

// Vector operations
let mut v = vec![1, 2, 3];
v.push(4);                  // Add to end
v.pop();                    // Remove & return last
v.len();                    // Length
v.is_empty();               // Check if empty
v.insert(0, 0);             // Insert at index
v.remove(0);                // Remove at index

// Accessing elements
let first = v[0];           // Panics if out of bounds
let first = v.get(0);       // Returns Option<&T>

// Iteration
for item in &v {
    println!("{}", item);
}

for item in &mut v {
    *item += 1;
}

// Slicing
let slice = &v[1..3];

HashMaps

use std::collections::HashMap;

// Creating HashMaps
let mut map: HashMap<String, i32> = HashMap::new();
let map2: HashMap<_, _> = vec![("a", 1), ("b", 2)]
    .into_iter().collect();

// HashMap operations
map.insert(String::from("key"), 10);
map.get("key");             // Returns Option<&V>
map.remove("key");          // Remove entry
map.contains_key("key");    // Check key exists
map.len();                  // Number of entries

// Entry API
map.entry(String::from("key")).or_insert(0);

// Iteration
for (key, value) in &map {
    println!("{}: {}", key, value);
}

Functions

// Basic function
fn greet(name: &str) -> String {
    format!("Hello, {}!", name)  // No semicolon = return
}

// Multiple parameters
fn add(a: i32, b: i32) -> i32 {
    a + b
}

// No return value (returns unit type)
fn print_value(x: i32) {
    println!("{}", x);
}

// Early return
fn check(x: i32) -> bool {
    if x < 0 {
        return false;
    }
    true
}

// Closures
let add_one = |x: i32| x + 1;
let add = |a, b| a + b;
let print = |x| println!("{}", x);

// Closure capturing
let multiplier = 3;
let multiply = |x| x * multiplier;

Control Flow

// If/else
if condition {
    // ...
} else if other {
    // ...
} else {
    // ...
}

// If as expression
let result = if condition { "yes" } else { "no" };

// Loop (infinite)
loop {
    break;  // Exit loop
}

// Loop with return value
let result = loop {
    break 42;
};

// While loop
while condition {
    // ...
}

// For loop
for i in 0..5 {          // 0, 1, 2, 3, 4
    println!("{}", i);
}

for i in 0..=5 {         // 0, 1, 2, 3, 4, 5
    println!("{}", i);
}

for (i, val) in v.iter().enumerate() {
    println!("{}: {}", i, val);
}

Pattern Matching

// Match expression
let x = 1;
match x {
    1 => println!("one"),
    2 | 3 => println!("two or three"),
    4..=10 => println!("four to ten"),
    _ => println!("other"),
}

// Match with binding
match x {
    n @ 1..=5 => println!("got {}", n),
    _ => (),
}

// Match on Option
match some_option {
    Some(value) => println!("{}", value),
    None => println!("nothing"),
}

// Match on Result
match some_result {
    Ok(value) => println!("{}", value),
    Err(e) => println!("Error: {}", e),
}

// If let (shorthand for single match)
if let Some(value) = some_option {
    println!("{}", value);
}

// While let
while let Some(value) = stack.pop() {
    println!("{}", value);
}

Ownership & Borrowing

// Ownership - each value has one owner
let s1 = String::from("hello");
let s2 = s1;      // s1 is moved, no longer valid
// println!("{}", s1); // Error!

// Clone (deep copy)
let s1 = String::from("hello");
let s2 = s1.clone();  // Both valid

// References (borrowing)
let s = String::from("hello");
let len = calculate_length(&s);  // Borrow
println!("{}", s);               // Still valid

fn calculate_length(s: &String) -> usize {
    s.len()
}

// Mutable references (only one at a time)
let mut s = String::from("hello");
change(&mut s);

fn change(s: &mut String) {
    s.push_str(", world");
}

// Slices (references to part of data)
let s = String::from("hello world");
let hello = &s[0..5];   // &str slice
let world = &s[6..11];

Structs

// Defining a struct
struct User {
    username: String,
    email: String,
    active: bool,
}

// Creating instances
let user = User {
    username: String::from("alice"),
    email: String::from("alice@example.com"),
    active: true,
};

// Shorthand (when variable names match)
let username = String::from("bob");
let user = User { username, ..user }; // Struct update syntax

// Tuple structs
struct Point(i32, i32, i32);
let origin = Point(0, 0, 0);

// Unit-like structs
struct AlwaysEqual;

// Methods with impl
impl User {
    // Associated function (constructor)
    fn new(username: String, email: String) -> Self {
        Self { username, email, active: true }
    }
    
    // Method (takes &self)
    fn is_active(&self) -> bool {
        self.active
    }
    
    // Mutable method
    fn deactivate(&mut self) {
        self.active = false;
    }
}

Enums

// Basic enum
enum Direction {
    North,
    South,
    East,
    West,
}

// Enum with data
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}

// Using enums
let msg = Message::Write(String::from("hello"));

// Option enum (built-in)
let some_number: Option<i32> = Some(5);
let no_number: Option<i32> = None;

// Result enum (built-in)
let success: Result<i32, String> = Ok(42);
let failure: Result<i32, String> = Err("error".to_string());

// Impl on enums
impl Message {
    fn call(&self) {
        // ...
    }
}

Error Handling

// Result type
fn read_file() -> Result<String, std::io::Error> {
    std::fs::read_to_string("file.txt")
}

// Handling Results
let content = read_file().unwrap();        // Panic on error
let content = read_file().expect("Failed"); // Panic with message
let content = read_file().unwrap_or_default(); // Default on error

// Match on Result
match read_file() {
    Ok(content) => println!("{}", content),
    Err(e) => println!("Error: {}", e),
}

// ? operator (propagate errors)
fn process() -> Result<String, std::io::Error> {
    let content = std::fs::read_to_string("file.txt")?;
    Ok(content.to_uppercase())
}

// Multiple error types
use std::error::Error;
fn run() -> Result<(), Box<dyn Error>> {
    let content = std::fs::read_to_string("file.txt")?;
    let num: i32 = content.trim().parse()?;
    Ok(())
}

Traits

// Defining a trait
trait Summary {
    fn summarize(&self) -> String;
    
    // Default implementation
    fn preview(&self) -> String {
        format!("Read more: {}", self.summarize())
    }
}

// Implementing a trait
struct Article { title: String, content: String }

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

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

// Multiple trait bounds
fn process<T: Summary + Clone>(item: &T) { }

// Where clause
fn some_fn<T, U>(t: &T, u: &U)
where
    T: Summary + Clone,
    U: Clone + std::fmt::Debug,
{ }

// Returning traits
fn create() -> impl Summary {
    Article { title: "...".to_string(), content: "...".to_string() }
}

Generics

// Generic function
fn largest<T: PartialOrd>(list: &[T]) -> &T {
    let mut largest = &list[0];
    for item in list {
        if item > largest {
            largest = item;
        }
    }
    largest
}

// Generic struct
struct Point<T> {
    x: T,
    y: T,
}

// Generic impl
impl<T> Point<T> {
    fn x(&self) -> &T {
        &self.x
    }
}

// Specific impl
impl Point<f64> {
    fn distance(&self) -> f64 {
        (self.x.powi(2) + self.y.powi(2)).sqrt()
    }
}

// Generic enum
enum Option<T> {
    Some(T),
    None,
}

Iterators

let v = vec![1, 2, 3, 4, 5];

// Iterator methods
v.iter()                    // Iterate over &T
v.iter_mut()                // Iterate over &mut T
v.into_iter()               // Take ownership

// Common iterator adaptors
v.iter().map(|x| x * 2)
v.iter().filter(|x| **x > 2)
v.iter().enumerate()
v.iter().take(3)
v.iter().skip(2)
v.iter().zip(other.iter())
v.iter().chain(other.iter())
v.iter().flatten()
v.iter().rev()

// Consuming adaptors
v.iter().collect::<Vec<_>>()
v.iter().sum::<i32>()
v.iter().fold(0, |acc, x| acc + x)
v.iter().any(|x| *x > 3)
v.iter().all(|x| *x > 0)
v.iter().find(|x| **x > 3)
v.iter().count()

Common Macros

// Printing
println!("Hello, {}!", name);   // Print with newline
print!("No newline");           // Print without newline
eprintln!("Error!");            // Print to stderr
dbg!(expression);               // Debug print

// Formatting
format!("Hello, {}!", name);    // Return String

// Vector creation
vec![1, 2, 3];

// Assertions
assert!(condition);
assert_eq!(a, b);
assert_ne!(a, b);

// Panic
panic!("Something went wrong!");
todo!();                        // Placeholder
unimplemented!();               // Not implemented
unreachable!();                 // Should never reach

// Derive macros
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct MyStruct { }

Lifetimes

// Lifetime annotation
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}

// Struct with lifetime
struct Excerpt<'a> {
    part: &'a str,
}

impl<'a> Excerpt<'a> {
    fn level(&self) -> i32 {
        3
    }
}

// Static lifetime (lives for entire program)
let s: &'static str = "I live forever";