📋

Rust Cheatsheet

Quick reference cards for common Rust patterns and syntax. Bookmark this page for instant access while coding.

12 Categories
46 Code Snippets
Copy & Paste Ready
🔐

Ownership & Borrowing

Ownership ve Borrowing

Move SemanticsOwnership & Borrowing
let s1 = String::from("hello");
let s2 = s1; // s1 is moved, no longer valid
// println!("{}", s1); // Error!
CloneOwnership & Borrowing
let s1 = String::from("hello");
let s2 = s1.clone(); // Deep copy
println!("{} {}", s1, s2); // Both valid
Immutable BorrowOwnership & Borrowing
let s = String::from("hello");
let r1 = &s; // Immutable borrow
let r2 = &s; // Multiple immutable OK
println!("{} {}", r1, r2);
Mutable BorrowOwnership & Borrowing
let mut s = String::from("hello");
let r = &mut s; // Mutable borrow
r.push_str(" world");
// Only one mutable borrow at a time
Lifetime AnnotationOwnership & Borrowing
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}
📦

Common Types

Yaygın Tipler

Option<T>Common Types
let some_value: Option<i32> = Some(5);
let no_value: Option<i32> = None;

// Pattern matching
match some_value {
    Some(v) => println!("Got: {}", v),
    None => println!("Nothing"),
}

// Or use if let
if let Some(v) = some_value {
    println!("Got: {}", v);
}
Result<T, E>Common Types
fn divide(a: i32, b: i32) -> Result<i32, String> {
    if b == 0 {
        Err("Division by zero".to_string())
    } else {
        Ok(a / b)
    }
}

// Use ? operator
fn calc() -> Result<i32, String> {
    let result = divide(10, 2)?;
    Ok(result * 2)
}
Vec<T>Common Types
let mut v: Vec<i32> = Vec::new();
let v2 = vec![1, 2, 3]; // Macro

v.push(1);
v.push(2);
let first = &v[0]; // May panic
let safe = v.get(0); // Returns Option
HashMap<K, V>Common Types
use std::collections::HashMap;

let mut map = HashMap::new();
map.insert("key", "value");

// Entry API
map.entry("key2").or_insert("default");

// Get value
if let Some(v) = map.get("key") {
    println!("{}", v);
}
🎯

Traits & Generics

Trait ve Generic'ler

Trait DefinitionTraits & Generics
trait Summary {
    fn summarize(&self) -> String;

    // Default implementation
    fn default_summary(&self) -> String {
        String::from("Read more...")
    }
}
Trait ImplementationTraits & Generics
struct Article { title: String, content: String }

impl Summary for Article {
    fn summarize(&self) -> String {
        format!("{}: {}", self.title, &self.content[..50])
    }
}
Trait BoundsTraits & Generics
// Generic function with trait bound
fn print_summary<T: Summary>(item: &T) {
    println!("{}", item.summarize());
}

// Multiple bounds
fn complex<T: Clone + Debug>(item: T) { }

// Where clause
fn verbose<T, U>(t: T, u: U) -> i32
where
    T: Display + Clone,
    U: Clone + Debug,
{ 42 }
impl TraitTraits & Generics
// Return type
fn make_iter() -> impl Iterator<Item = i32> {
    (0..10).filter(|x| x % 2 == 0)
}

// Parameter type (same as generic)
fn process(item: impl Summary) {
    println!("{}", item.summarize());
}
🎯

Smart Pointers

Akıllı İşaretçiler

Box<T>Smart Pointers
// Heap allocation
let b = Box::new(5);
println!("{}", b);

// Recursive types
enum List {
    Cons(i32, Box<List>),
    Nil,
}
Rc<T>Smart Pointers
use std::rc::Rc;

let a = Rc::new(5);
let b = Rc::clone(&a); // Increment ref count
let c = Rc::clone(&a);

println!("Count: {}", Rc::strong_count(&a)); // 3
Arc<T>Smart Pointers
use std::sync::Arc;
use std::thread;

let data = Arc::new(vec![1, 2, 3]);

let handles: Vec<_> = (0..3).map(|_| {
    let data = Arc::clone(&data);
    thread::spawn(move || {
        println!("{:?}", data);
    })
}).collect();
RefCell<T>Smart Pointers
use std::cell::RefCell;

let data = RefCell::new(5);

// Borrow at runtime
*data.borrow_mut() += 1;
println!("{}", data.borrow()); // 6

// Interior mutability pattern
let shared = Rc::new(RefCell::new(vec![]));

Async/Await

Async/Await

Async FunctionAsync/Await
async fn fetch_data() -> Result<String, Error> {
    let response = reqwest::get("https://api.example.com")
        .await?;
    response.text().await
}
Spawn TasksAsync/Await
use tokio;

#[tokio::main]
async fn main() {
    let handle = tokio::spawn(async {
        // Async work
        42
    });

    let result = handle.await.unwrap();
}
SelectAsync/Await
use tokio::select;

async fn race() {
    select! {
        result = async_op1() => {
            println!("Op1: {:?}", result);
        }
        result = async_op2() => {
            println!("Op2: {:?}", result);
        }
    }
}
JoinAsync/Await
use tokio::join;

async fn parallel() {
    let (r1, r2, r3) = join!(
        fetch_user(1),
        fetch_user(2),
        fetch_user(3),
    );
    // All complete concurrently
}
🚨

Error Handling

Hata Yönetimi

Custom ErrorError Handling
use thiserror::Error;

#[derive(Error, Debug)]
enum MyError {
    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),

    #[error("Parse error: {msg}")]
    Parse { msg: String },

    #[error("Not found")]
    NotFound,
}
? OperatorError Handling
fn read_file() -> Result<String, MyError> {
    let content = std::fs::read_to_string("file.txt")?;
    let parsed = content.parse::<i32>()
        .map_err(|e| MyError::Parse {
            msg: e.to_string()
        })?;
    Ok(parsed.to_string())
}
Result CombinatorsError Handling
let result: Result<i32, &str> = Ok(5);

// Transform Ok value
let doubled = result.map(|x| x * 2);

// Transform Err value
let mapped_err = result.map_err(|e| format!("Error: {}", e));

// Chain operations
let chained = result
    .and_then(|x| if x > 0 { Ok(x) } else { Err("negative") });
anyhowError Handling
use anyhow::{Context, Result, bail};

fn process() -> Result<()> {
    let file = std::fs::read_to_string("config.toml")
        .context("Failed to read config")?;

    if file.is_empty() {
        bail!("Config file is empty");
    }

    Ok(())
}
🔄

Iterators

Iterator'lar

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

// Transform
let doubled: Vec<_> = v.iter().map(|x| x * 2).collect();

// Filter
let evens: Vec<_> = v.iter().filter(|x| *x % 2 == 0).collect();

// Find
let first_even = v.iter().find(|x| *x % 2 == 0);

// Fold/Reduce
let sum: i32 = v.iter().sum();
let product: i32 = v.iter().product();
ChainingIterators
let result: Vec<i32> = (0..100)
    .filter(|x| x % 3 == 0)
    .map(|x| x * x)
    .take(5)
    .collect();
// [0, 9, 36, 81, 144]
Custom IteratorIterators
struct Counter { count: u32 }

impl Iterator for Counter {
    type Item = u32;

    fn next(&mut self) -> Option<Self::Item> {
        self.count += 1;
        if self.count < 6 {
            Some(self.count)
        } else {
            None
        }
    }
}
🎨

Macros

Makrolar

Declarative MacroMacros
macro_rules! say_hello {
    () => {
        println!("Hello!");
    };
    ($name:expr) => {
        println!("Hello, {}!", $name);
    };
}

say_hello!();         // Hello!
say_hello!("World");  // Hello, World!
RepetitionMacros
macro_rules! vec_of_strings {
    ($($x:expr),* $(,)?) => {
        vec![$($x.to_string()),*]
    };
}

let v = vec_of_strings!["a", "b", "c"];
// vec!["a".to_string(), "b".to_string(), "c".to_string()]
Derive MacroMacros
use serde::{Serialize, Deserialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
struct User {
    name: String,
    email: String,
}

// Generates impl blocks automatically
🔀

Concurrency

Eşzamanlılık

ThreadsConcurrency
use std::thread;

let handle = thread::spawn(|| {
    println!("Hello from thread!");
    42
});

let result = handle.join().unwrap(); // 42
ChannelsConcurrency
use std::sync::mpsc;

let (tx, rx) = mpsc::channel();

thread::spawn(move || {
    tx.send("Hello").unwrap();
});

let msg = rx.recv().unwrap(); // "Hello"
MutexConcurrency
use std::sync::{Arc, Mutex};

let counter = Arc::new(Mutex::new(0));

let handles: Vec<_> = (0..10).map(|_| {
    let counter = Arc::clone(&counter);
    thread::spawn(move || {
        let mut num = counter.lock().unwrap();
        *num += 1;
    })
}).collect();

for handle in handles {
    handle.join().unwrap();
}
RwLockConcurrency
use std::sync::RwLock;

let lock = RwLock::new(5);

// Multiple readers
{
    let r1 = lock.read().unwrap();
    let r2 = lock.read().unwrap();
}

// One writer
{
    let mut w = lock.write().unwrap();
    *w += 1;
}
🧪

Testing

Test

Unit TestsTesting
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_add() {
        assert_eq!(add(2, 2), 4);
    }

    #[test]
    #[should_panic(expected = "overflow")]
    fn test_panic() {
        panic!("overflow");
    }
}
Result in TestsTesting
#[test]
fn test_with_result() -> Result<(), String> {
    let value = some_fallible_op()?;
    assert_eq!(value, 42);
    Ok(())
}
AssertionsTesting
assert!(condition);
assert_eq!(left, right);
assert_ne!(left, right);

// With message
assert!(x > 0, "x must be positive, got {}", x);

// Debug assertions (only in debug builds)
debug_assert!(expensive_check());
🎨

Design Patterns

Tasarım Kalıpları

Builder PatternDesign Patterns
struct ServerBuilder {
    host: String,
    port: u16,
}

impl ServerBuilder {
    fn new() -> Self {
        Self { host: "localhost".into(), port: 8080 }
    }

    fn host(mut self, host: &str) -> Self {
        self.host = host.into();
        self
    }

    fn port(mut self, port: u16) -> Self {
        self.port = port;
        self
    }

    fn build(self) -> Server {
        Server { host: self.host, port: self.port }
    }
}

// Usage: ServerBuilder::new().host("0.0.0.0").port(3000).build()
Factory PatternDesign Patterns
trait Shape { fn area(&self) -> f64; }

struct Circle { radius: f64 }
struct Rectangle { width: f64, height: f64 }

impl Shape for Circle {
    fn area(&self) -> f64 { 3.14159 * self.radius * self.radius }
}

impl Shape for Rectangle {
    fn area(&self) -> f64 { self.width * self.height }
}

fn create_shape(kind: &str) -> Box<dyn Shape> {
    match kind {
        "circle" => Box::new(Circle { radius: 1.0 }),
        "rectangle" => Box::new(Rectangle { width: 2.0, height: 3.0 }),
        _ => panic!("Unknown shape"),
    }
}
Strategy PatternDesign Patterns
trait CompressionStrategy {
    fn compress(&self, data: &[u8]) -> Vec<u8>;
}

struct GzipCompression;
struct LzmaCompression;

impl CompressionStrategy for GzipCompression {
    fn compress(&self, data: &[u8]) -> Vec<u8> {
        // gzip logic
        data.to_vec()
    }
}

struct Compressor<S: CompressionStrategy> {
    strategy: S,
}

impl<S: CompressionStrategy> Compressor<S> {
    fn compress(&self, data: &[u8]) -> Vec<u8> {
        self.strategy.compress(data)
    }
}
Observer PatternDesign Patterns
use std::sync::mpsc::{channel, Sender, Receiver};

struct EventBus<T> {
    senders: Vec<Sender<T>>,
}

impl<T: Clone> EventBus<T> {
    fn new() -> Self {
        Self { senders: vec![] }
    }

    fn subscribe(&mut self) -> Receiver<T> {
        let (tx, rx) = channel();
        self.senders.push(tx);
        rx
    }

    fn publish(&self, event: T) {
        for sender in &self.senders {
            let _ = sender.send(event.clone());
        }
    }
}
🌳

Data Structures

Veri Yapıları

Binary Search TreeData Structures
struct Node<T> {
    value: T,
    left: Option<Box<Node<T>>>,
    right: Option<Box<Node<T>>>,
}

impl<T: Ord> Node<T> {
    fn insert(&mut self, value: T) {
        if value < self.value {
            match &mut self.left {
                Some(left) => left.insert(value),
                None => self.left = Some(Box::new(Node {
                    value, left: None, right: None
                })),
            }
        } else {
            match &mut self.right {
                Some(right) => right.insert(value),
                None => self.right = Some(Box::new(Node {
                    value, left: None, right: None
                })),
            }
        }
    }
}
Graph (Adjacency List)Data Structures
struct Graph {
    edges: Vec<Vec<usize>>,
}

impl Graph {
    fn new(n: usize) -> Self {
        Self { edges: vec![vec![]; n] }
    }

    fn add_edge(&mut self, from: usize, to: usize) {
        self.edges[from].push(to);
    }

    fn bfs(&self, start: usize) -> Vec<usize> {
        use std::collections::VecDeque;
        let mut visited = vec![false; self.edges.len()];
        let mut queue = VecDeque::from([start]);
        let mut order = vec![];

        while let Some(node) = queue.pop_front() {
            if visited[node] { continue; }
            visited[node] = true;
            order.push(node);
            for &neighbor in &self.edges[node] {
                queue.push_back(neighbor);
            }
        }
        order
    }
}
Dynamic ProgrammingData Structures
use std::collections::HashMap;

// Fibonacci with memoization
fn fib(n: u64, cache: &mut HashMap<u64, u64>) -> u64 {
    if n <= 1 { return n; }
    if let Some(&result) = cache.get(&n) {
        return result;
    }
    let result = fib(n - 1, cache) + fib(n - 2, cache);
    cache.insert(n, result);
    result
}

// Tabulation (bottom-up)
fn fib_tab(n: usize) -> u64 {
    if n <= 1 { return n as u64; }
    let mut dp = vec![0u64; n + 1];
    dp[1] = 1;
    for i in 2..=n {
        dp[i] = dp[i-1] + dp[i-2];
    }
    dp[n]
}
LRU CacheData Structures
use std::collections::HashMap;

struct LRUCache<K, V> {
    capacity: usize,
    map: HashMap<K, V>,
    order: Vec<K>,
}

impl<K: Eq + std::hash::Hash + Clone, V> LRUCache<K, V> {
    fn new(capacity: usize) -> Self {
        Self { capacity, map: HashMap::new(), order: vec![] }
    }

    fn get(&mut self, key: &K) -> Option<&V> {
        if self.map.contains_key(key) {
            self.order.retain(|k| k != key);
            self.order.push(key.clone());
        }
        self.map.get(key)
    }

    fn put(&mut self, key: K, value: V) {
        if self.map.len() >= self.capacity && !self.map.contains_key(&key) {
            if let Some(oldest) = self.order.first().cloned() {
                self.map.remove(&oldest);
                self.order.remove(0);
            }
        }
        self.order.retain(|k| k != &key);
        self.order.push(key.clone());
        self.map.insert(key, value);
    }
}

Want More Depth?

Explore comprehensive tutorials with full working examples in the Software Engineering section.