Rust logo
Rust Cheatsheet
25 of 25 code examples
.rs
Hello World
Basic Rust program
Basics
fn main() {
    println!("Hello, World!");
}
Variables
Variable declarations and mutability
Basics
// Immutable by default
let x = 5;
let name = "Alice";

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

// Constants
const MAX_POINTS: u32 = 100_000;
const PI: f64 = 3.14159;

// Shadowing
let spaces = "   ";
let spaces = spaces.len(); // Different type

// Type annotations
let age: u32 = 30;
let price: f64 = 19.99;
let is_active: bool = true;
Data Types
Basic data types
Basics
// Scalar types
let integer: i32 = 42;
let unsigned: u64 = 100;
let float: f64 = 3.14;
let boolean: bool = true;
let character: char = '🦀';

// Compound types
let tuple: (i32, f64, char) = (500, 6.4, 'z');
let (x, y, z) = tuple; // Destructuring

// Arrays
let array: [i32; 5] = [1, 2, 3, 4, 5];
let first = array[0];
let sized_array = [0; 10]; // 10 zeros

// Slices
let slice = &array[1..4]; // [2, 3, 4]
Functions
Function definitions
Functions
// Basic function
fn add(x: i32, y: i32) -> i32 {
    x + y
}

// Expression vs statement
fn five() -> i32 {
    5 // No semicolon = expression (return value)
}

// Multiple parameters
fn print_coordinates(x: i32, y: i32) {
    println!("Coordinates: ({}, {})", x, y);
}

// Early return
fn safe_divide(a: f64, b: f64) -> Option<f64> {
    if b == 0.0 {
        return None;
    }
    Some(a / b)
}

// Function pointers
fn apply<F>(f: F, x: i32) -> i32 
where 
    F: Fn(i32) -> i32 
{
    f(x)
}
Control Flow
If, loops, and match
Basics
// If expressions
let number = 6;
if number % 2 == 0 {
    println!("even");
} else {
    println!("odd");
}

// If as expression
let result = if number > 5 { "big" } else { "small" };

// Loop
let mut counter = 0;
loop {
    counter += 1;
    if counter == 10 {
        break counter * 2;
    }
}

// While loop
while counter > 0 {
    println!("{}!", counter);
    counter -= 1;
}

// For loop
for element in [10, 20, 30].iter() {
    println!("{}", element);
}

// Match expression
match number {
    1 => println!("One"),
    2 | 3 | 5 | 7 => println!("Prime"),
    _ => println!("Other"),
}
Ownership
Rust's ownership system
Memory
// Ownership transfer
let s1 = String::from("hello");
let s2 = s1; // s1 is no longer valid here
// println!("{}", s1); // This would cause compile error

// Clone for deep copy
let s3 = s2.clone();
println!("s2 = {}, s3 = {}", s2, s3);

// Function and ownership
fn take_ownership(s: String) {
    println!("{}", s);
} // s goes out of scope and memory is freed

let s4 = String::from("hello");
take_ownership(s4);
// s4 is no longer valid here

// Return ownership
fn give_ownership() -> String {
    let s = String::from("hello");
    s // Ownership is returned
}
References & Borrowing
Borrowing and references
Memory
// Immutable reference
fn calculate_length(s: &String) -> usize {
    s.len()
} // s goes out of scope but doesn't drop what it refers to

let s1 = String::from("hello");
let len = calculate_length(&s1);
println!("Length of '{}' is {}", s1, len);

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

let mut s2 = String::from("hello");
change(&mut s2);
println!("{}", s2);

// Only one mutable reference
let mut s = String::from("hello");
let r1 = &mut s;
// let r2 = &mut s; // This would cause compile error

// Multiple immutable references allowed
let r3 = &s;
let r4 = &s;
println!("{} and {}", r3, r4);
Structs
Custom data types
Data Structures
// Struct definition
struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}

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

// Mutable struct
let mut user2 = User {
    email: String::from("another@example.com"),
    username: String::from("user456"),
    active: true,
    sign_in_count: 1,
};
user2.sign_in_count = 2;

// Field init shorthand
fn build_user(email: String, username: String) -> User {
    User {
        email,
        username,
        active: true,
        sign_in_count: 1,
    }
}

// Tuple structs
struct Color(i32, i32, i32);
let black = Color(0, 0, 0);
Enums & Pattern Matching
Enums and match expressions
Data Structures
// Basic enum
enum IpAddrKind {
    V4,
    V6,
}

// Enum with data
enum IpAddr {
    V4(u8, u8, u8, u8),
    V6(String),
}

let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V6(String::from("::1"));

// Option enum
fn divide(numerator: f64, denominator: f64) -> Option<f64> {
    if denominator == 0.0 {
        None
    } else {
        Some(numerator / denominator)
    }
}

// Match expression
match divide(10.0, 2.0) {
    Some(result) => println!("Result: {}", result),
    None => println!("Cannot divide by zero!"),
}

// if let syntax
let some_value = Some(3);
if let Some(3) = some_value {
    println!("three");
}
Methods & Traits
Implementing methods and traits
OOP
// Method implementation
struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    // Associated function (like static method)
    fn square(size: u32) -> Rectangle {
        Rectangle {
            width: size,
            height: size,
        }
    }
    
    // Method with &self
    fn area(&self) -> u32 {
        self.width * self.height
    }
    
    // Method with &mut self
    fn double(&mut self) {
        self.width *= 2;
        self.height *= 2;
    }
}

// Using methods
let mut rect = Rectangle { width: 30, height: 50 };
println!("Area: {}", rect.area());
rect.double();

// Trait definition
trait Summary {
    fn summarize(&self) -> String;
    
    // Default implementation
    fn preview(&self) -> String {
        String::from("(Read more...)")
    }
}

// Trait implementation
struct NewsArticle {
    headline: String,
    location: String,
    author: String,
}

impl Summary for NewsArticle {
    fn summarize(&self) -> String {
        format!("{}, by {} ({})", self.headline, self.author, self.location)
    }
}
Generics
Generic types and functions
Advanced
// 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,
}

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

// Multiple generic types
struct Pair<T, U> {
    first: T,
    second: U,
}

// Using generics
let integer_point = Point { x: 5, y: 10 };
let float_point = Point { x: 1.0, y: 4.0 };

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

// where clause for complex bounds
fn some_function<T, U>(t: &T, u: &U) -> i32 
where 
    T: Display + Clone,
    U: Clone + Debug,
{
    // function body
    42
}
Error Handling
Result and Option types
Error Handling
// Result enum
enum Result<T, E> {
    Ok(T),
    Err(E),
}

// Using Result
fn read_file(path: &str) -> Result<String, std::io::Error> {
    std::fs::read_to_string(path)
}

// Handling errors with match
match read_file("hello.txt") {
    Ok(content) => println!("File content: {}", content),
    Err(error) => println!("Error reading file: {}", error),
}

// Using ? operator for propagation
fn read_username_from_file() -> Result<String, std::io::Error> {
    let mut s = String::new();
    std::fs::File::open("hello.txt")?.read_to_string(&mut s)?;
    Ok(s)
}

// Option type
fn find_user(id: u32) -> Option<String> {
    if id == 1 {
        Some(String::from("Alice"))
    } else {
        None
    }
}

// Combinators
let username = find_user(1)
    .map(|name| name.to_uppercase())
    .unwrap_or(String::from("Unknown"));

// unwrap and expect
let file = std::fs::File::open("config.txt").expect("Failed to open config file");
Collections - Vector
Dynamic arrays
Data Structures
// Creating vectors
let mut v: Vec<i32> = Vec::new();
v.push(1);
v.push(2);
v.push(3);

// Vector macro
let mut v2 = vec![1, 2, 3];
v2.push(4);

// Accessing elements
let third = &v2[2]; // Panics if out of bounds
println!("The third element is {}", third);

let third = v2.get(2); // Returns Option
match third {
    Some(value) => println!("The third element is {}", value),
    None => println!("There is no third element"),
}

// Iterating
for i in &v2 {
    println!("{}", i);
}

// Iterating mutable
for i in &mut v2 {
    *i += 50;
}

// Using enums for multiple types
enum SpreadsheetCell {
    Int(i32),
    Float(f64),
    Text(String),
}

let row = vec![
    SpreadsheetCell::Int(3),
    SpreadsheetCell::Text(String::from("blue")),
    SpreadsheetCell::Float(10.12),
];
Collections - String
String manipulation
Data Structures
// Creating strings
let mut s = String::new();
let data = "initial contents";
let s = data.to_string();
let s = String::from("initial contents");

// Updating strings
let mut s = String::from("foo");
s.push_str("bar");
s.push('!');

// Concatenation
let s1 = String::from("Hello, ");
let s2 = String::from("world!");
let s3 = s1 + &s2; // Note: s1 has been moved here

// Format macro
let s1 = String::from("tic");
let s2 = String::from("tac");
let s3 = String::from("toe");
let s = format!("{}-{}-{}", s1, s2, s3);

// Indexing (not directly supported)
let hello = "Здравствуйте";
// let h = hello[0]; // This won't compile!

// Slicing strings
let s = &hello[0..4]; // First 2 characters (4 bytes)
println!("{}", s); // Prints "Зд"

// Iterating over strings
for c in "नमस्ते".chars() {
    println!("{}", c);
}

for b in "नमस्ते".bytes() {
    println!("{}", b);
}
Collections - HashMap
Key-value storage
Data Structures
use std::collections::HashMap;

// Creating HashMap
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);

// Accessing values
let team_name = String::from("Blue");
let score = scores.get(&team_name);

match score {
    Some(value) => println!("Score: {}", value),
    None => println!("Team not found"),
}

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

// Updating values
// Overwriting
scores.insert(String::from("Blue"), 25);

// Only insert if key has no value
scores.entry(String::from("Yellow")).or_insert(50);
scores.entry(String::from("Red")).or_insert(30);

// Update based on old value
let text = "hello world wonderful world";
let mut map = HashMap::new();

for word in text.split_whitespace() {
    let count = map.entry(word).or_insert(0);
    *count += 1;
}

println!("{:?}", map);
Lifetimes
Explicit lifetime annotations
Advanced
// Lifetime annotations
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

// Using the function
let string1 = String::from("abcd");
let string2 = "xyz";

let result = longest(string1.as_str(), string2);
println!("The longest string is {}", result);

// Lifetime in structs
struct ImportantExcerpt<'a> {
    part: &'a str,
}

impl<'a> ImportantExcerpt<'a> {
    fn level(&self) -> i32 {
        3
    }
    
    fn announce_and_return_part(&self, announcement: &str) -> &str {
        println!("Attention please: {}", announcement);
        self.part
    }
}

// Using the struct
let novel = String::from("Call me Ishmael. Some years ago...");
let first_sentence = novel.split('.').next().expect("Could not find a '.'");
let i = ImportantExcerpt {
    part: first_sentence,
};

// Static lifetime
let s: &'static str = "I have a static lifetime.";

// Lifetime elision rules
fn first_word(s: &str) -> &str { // Compiler infers lifetimes
    let bytes = s.as_bytes();
    
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }
    
    &s[..]
}
Closures
Anonymous functions
Functions
// Basic closure
let add_one = |x: i32| -> i32 { x + 1 };
let result = add_one(5);

// Type inference
let add_one_v2 = |x| x + 1;
let result2 = add_one_v2(5);

// Capturing environment
let x = 4;
let equal_to_x = |z| z == x;
let y = 4;
assert!(equal_to_x(y));

// Move closure (takes ownership)
let x = vec![1, 2, 3];
let equal_to_x = move |z| z == x;
// println!("{:?}", x); // Error: x was moved

// Closure traits
// Fn: borrows values immutably
// FnMut: borrows values mutably  
// FnOnce: takes ownership

// Using closures with iterators
let v1 = vec![1, 2, 3];
let v2: Vec<_> = v1.iter().map(|x| x + 1).collect();
assert_eq!(v2, vec![2, 3, 4]);

// Storing closures
struct Cacher<T>
where
    T: Fn(u32) -> u32,
{
    calculation: T,
    value: Option<u32>,
}

impl<T> Cacher<T>
where
    T: Fn(u32) -> u32,
{
    fn new(calculation: T) -> Cacher<T> {
        Cacher {
            calculation,
            value: None,
        }
    }
}
Iterators
Iterator pattern and methods
Data Structures
// Creating iterators
let v1 = vec![1, 2, 3];
let v1_iter = v1.iter();

// Using iterators
for val in v1_iter {
    println!("Got: {}", val);
}

// Iterator adapters (lazy)
let v1: Vec<i32> = vec![1, 2, 3];
let v2: Vec<_> = v1.iter().map(|x| x + 1).collect();

// Consumer methods
let total: i32 = v1.iter().sum();
let any_gt_2 = v1.iter().any(|&x| x > 2);
let all_gt_0 = v1.iter().all(|&x| x > 0);

// Chaining methods
let names = vec!["alice", "bob", "carol"];
let uppercase_names: Vec<String> = names
    .iter()
    .map(|s| s.to_uppercase())
    .filter(|s| s.len() > 3)
    .collect();

// Implementing Iterator trait
struct Counter {
    count: u32,
}

impl Counter {
    fn new() -> Counter {
        Counter { count: 0 }
    }
}

impl Iterator for Counter {
    type Item = u32;
    
    fn next(&mut self) -> Option<Self::Item> {
        if self.count < 5 {
            self.count += 1;
            Some(self.count)
        } else {
            None
        }
    }
}

// Using custom iterator
let mut counter = Counter::new();
while let Some(count) = counter.next() {
    println!("{}", count);
}
Smart Pointers
Box, Rc, Arc, RefCell
Memory
// Box - heap allocation
let b = Box::new(5);
println!("b = {}", b);

// Recursive types with Box
enum List {
    Cons(i32, Box<List>),
    Nil,
}

use List::{Cons, Nil};
let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil)))));

// Rc - reference counting
use std::rc::Rc;

let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
let b = Cons(3, Rc::clone(&a));
let c = Cons(4, Rc::clone(&a));

// Arc - atomic reference counting (thread-safe)
use std::sync::Arc;
use std::thread;

let counter = Arc::new(0);
let mut handles = vec![];

for _ in 0..10 {
    let counter = Arc::clone(&counter);
    let handle = thread::spawn(move || {
        // Use counter
    });
    handles.push(handle);
}

// RefCell - interior mutability
use std::cell::RefCell;

let x = RefCell::new(42);
{
    let mut y = x.borrow_mut();
    *y += 1;
}
println!("{:?}", x.borrow());
Concurrency - Threads
Spawning threads
Concurrency
use std::thread;
use std::time::Duration;

// Spawning a thread
let handle = thread::spawn(|| {
    for i in 1..10 {
        println!("hi number {} from the spawned thread!", i);
        thread::sleep(Duration::from_millis(1));
    }
});

// Main thread work
for i in 1..5 {
    println!("hi number {} from the main thread!", i);
    thread::sleep(Duration::from_millis(1));
}

// Wait for thread to finish
handle.join().unwrap();

// Moving ownership into thread
let v = vec![1, 2, 3];
let handle = thread::spawn(move || {
    println!("Here's a vector: {:?}", v);
});
handle.join().unwrap();
Concurrency - Channels
Message passing between threads
Concurrency
use std::sync::mpsc;
use std::thread;

// Multiple Producer, Single Consumer
let (tx, rx) = mpsc::channel();

// Spawn thread and send message
thread::spawn(move || {
    let val = String::from("hi");
    tx.send(val).unwrap();
});

// Receive message
let received = rx.recv().unwrap();
println!("Got: {}", received);

// Multiple messages
let (tx, rx) = mpsc::channel();
let tx1 = mpsc::Sender::clone(&tx);

thread::spawn(move || {
    let vals = vec![
        String::from("hi"),
        String::from("from"),
        String::from("the"),
        String::from("thread"),
    ];

    for val in vals {
        tx1.send(val).unwrap();
        thread::sleep(Duration::from_millis(100));
    }
});

thread::spawn(move || {
    let vals = vec![
        String::from("more"),
        String::from("messages"),
        String::from("for"),
        String::from("you"),
    ];

    for val in vals {
        tx.send(val).unwrap();
        thread::sleep(Duration::from_millis(100));
    }
});

for received in rx {
    println!("Got: {}", received);
}
Concurrency - Mutex
Shared state concurrency
Concurrency
use std::sync::{Mutex, Arc};
use std::thread;

// Single-threaded mutex
let m = Mutex::new(5);
{
    let mut num = m.lock().unwrap();
    *num = 6;
}
println!("m = {:?}", m);

// Multi-threaded mutex with Arc
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];

for _ in 0..10 {
    let counter = Arc::clone(&counter);
    let handle = thread::spawn(move || {
        let mut num = counter.lock().unwrap();
        *num += 1;
    });
    handles.push(handle);
}

for handle in handles {
    handle.join().unwrap();
}

println!("Result: {}", *counter.lock().unwrap());

// Deadlock prevention
let mutex1 = Arc::new(Mutex::new(0));
let mutex2 = Arc::new(Mutex::new(0));

// Always acquire locks in the same order
let handle1 = {
    let m1 = Arc::clone(&mutex1);
    let m2 = Arc::clone(&mutex2);
    thread::spawn(move || {
        let _lock1 = m1.lock().unwrap();
        let _lock2 = m2.lock().unwrap();
        // critical section
    })
};
Modules & Crates
Code organization
Packages
// Cargo.toml
// [package]
// name = "my_crate"
// version = "0.1.0"
// edition = "2021"
//
// [dependencies]
// serde = "1.0"

// src/lib.rs
mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
        fn seat_at_table() {}
    }
    
    mod serving {
        fn take_order() {}
        fn serve_order() {}
        fn take_payment() {}
    }
}

pub use crate::front_of_house::hosting;

pub fn eat_at_restaurant() {
    hosting::add_to_waitlist();
}

// src/main.rs
use my_crate::eat_at_restaurant;

fn main() {
    eat_at_restaurant();
}

// File hierarchy
// src/
//   lib.rs
//   main.rs
//   front_of_house.rs
//   front_of_house/
//     hosting.rs
//     serving.rs
Real-World: Web Server
Async web server with Tokio
Real World
use std::convert::Infallible;
use std::net::SocketAddr;
use hyper::{Body, Request, Response, Server};
use hyper::service::{make_service_fn, service_fn};

async fn hello_world(_req: Request<Body>) -> Result<Response<Body>, Infallible> {
    Ok(Response::new(Body::from("Hello, World!")))
}

#[tokio::main]
async fn main() {
    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    
    let make_svc = make_service_fn(|_conn| {
        async {
            Ok::<_, Infallible>(service_fn(hello_world))
        }
    });
    
    let server = Server::bind(&addr).serve(make_svc);
    
    println!("Server running on http://{}", addr);
    
    if let Err(e) = server.await {
        eprintln!("server error: {}", e);
    }
}

// With routing (using warp)
// use warp::Filter;
//
// #[tokio::main]
// async fn main() {
//     let hello = warp::path!("hello" / String)
//         .map(|name| format!("Hello, {}!", name));
//     
//     warp::serve(hello)
//         .run(([127, 0, 0, 1], 3030))
//         .await;
// }
Real-World: CLI Tool
Command-line application
Real World
use std::env;
use std::fs;
use std::process;

fn main() {
    let args: Vec<String> = env::args().collect();
    
    let config = Config::new(&args).unwrap_or_else(|err| {
        eprintln!("Problem parsing arguments: {}", err);
        process::exit(1);
    });
    
    if let Err(e) = run(config) {
        eprintln!("Application error: {}", e);
        process::exit(1);
    }
}

struct Config {
    query: String,
    filename: String,
    case_sensitive: bool,
}

impl Config {
    fn new(args: &[String]) -> Result<Config, &'static str> {
        if args.len() < 3 {
            return Err("not enough arguments");
        }
        
        let query = args[1].clone();
        let filename = args[2].clone();
        let case_sensitive = env::var("CASE_INSENSITIVE").is_err();
        
        Ok(Config {
            query,
            filename,
            case_sensitive,
        })
    }
}

fn run(config: Config) -> Result<(), Box<dyn std::error::Error>> {
    let contents = fs::read_to_string(config.filename)?;
    
    let results = if config.case_sensitive {
        search(&config.query, &contents)
    } else {
        search_case_insensitive(&config.query, &contents)
    };
    
    for line in results {
        println!("{}", line);
    }
    
    Ok(())
}

fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
    contents
        .lines()
        .filter(|line| line.contains(query))
        .collect()
}

fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
    let query = query.to_lowercase();
    contents
        .lines()
        .filter(|line| line.to_lowercase().contains(&query))
        .collect()
}