# Rust Cheat Sheet: Cargo, Ownership, Enums and Errors

Source: https://srvscripts.com/guides/rust-cheat-sheet/
Updated: 2026-10-09
Publisher: srvScripts (https://srvscripts.com/)

**Short answer:** Create a project with `cargo new app`, run it with `cargo run` and ship `cargo build --release`. Variables are immutable unless marked `mut`; each value has one owner, and you lend it with `&` (shared) or `&mut` (exclusive). Model states with `enum` and `match`, return `Result<T, E>` from anything that can fail and propagate errors with `?`. Use `Vec`, `HashMap` and iterator chains such as `.iter().map().filter().collect()`.

The examples in this Rust cheat sheet were run with Rust 1.99.0 and Cargo 1.99.0 (edition 2024) on Linux on 9 October 2026, and checked with `cargo clippy`.

## rustup and cargo commands

| Command | What it does |
| --- | --- |
| rustup update stable | Install or update the stable toolchain |
| cargo new app / cargo new --lib mylib | New binary or library project (edition 2024 by default) |
| cargo run -- --verbose | Build and run; arguments after -- go to your program |
| cargo build --release | Optimised binary in target/release/ |
| cargo check | Type-check without producing a binary (fast) |
| cargo test | Run unit, integration and doc tests |
| cargo clippy | Lints for common mistakes |
| cargo fmt | Format the code with rustfmt |
| cargo add serde --features derive | Add a dependency to Cargo.toml |
| cargo update | Update Cargo.lock within allowed versions |
| cargo doc --open | Build and open documentation for your crate and dependencies |
| rustup target add x86_64-unknown-linux-musl | Add a target for static Linux binaries (cargo build --target ...) |

## Variables, types and control flow

| Syntax | What it does | Example |
| --- | --- | --- |
| let x = 5; | Immutable binding | let name = "web01"; |
| let mut y = 10; | Mutable binding | y += 1; |
| let x = x * 2; | Shadowing: a new variable with the same name |  |
| i32, u16, u64, usize, f64, bool, char | Scalar types | let port: u16 = 443; |
| (i32, &str) / [u8; 4] | Tuple / fixed-size array | t.0, [0u8; 4] |
| const MAX: u32 = 100; | Compile-time constant (type required) |  |
| if as an expression | Returns a value | let k = if y > 10 { "big" } else { "small" }; |
| loop { break value; } | Infinite loop that can return a value |  |
| for i in 0..3 / 0..=3 / (1..=3).rev() | Exclusive, inclusive and reversed ranges |  |
| while cond { } | While loop |  |
| match code { 200 => .., 400..=499 => .., _ => .. } | Exhaustive pattern match |  |
| println!("{x} {:?} {:#?}", v, s) | Inline names, Debug and pretty Debug output |  |

## Ownership, borrowing and lifetimes

| Rule or syntax | Meaning |
| --- | --- |
| One owner per value | When the owner goes out of scope, the value is dropped |
| let s3 = s1; | Move: s1 can no longer be used (for String, Vec and other heap types) |
| s1.clone() | Explicit deep copy |
| Copy types (i32, bool, char, …) | Copied, not moved |
| &T | Shared borrow; any number at once |
| &mut T | Exclusive borrow; only one, and no shared borrows at the same time |
| String vs &str | Owned, growable text vs a borrowed string slice. Take &str in parameters |
| fn longest &str {
    s.split_whitespace().next().unwrap_or("")
}

let mut s = String::from("ab");
{
    let r = &mut s;      // exclusive borrow
    r.push('c');
}                        // borrow ends here
println!("{} {}", s, first_word("hello world"));   // abc hello
```

## Structs, enums and pattern matching

```
#[derive(Debug, Clone, PartialEq)]
struct Server {
    name: String,
    port: u16,
}

impl Server {
    fn new(name: &str, port: u16) -> Self {
        Self { name: name.to_string(), port }
    }
    fn set_port(&mut self, port: u16) { self.port = port; }
}

enum Event {
    Login { user: String, ok: bool },
    Restart(String),
    Quit,
}

for e in &events {
    match e {
        Event::Login { user, ok: false } => println!("failed login {user}"),
        Event::Login { user, .. } => println!("login {user}"),
        Event::Restart(svc) => println!("restart {svc}"),
        Event::Quit => println!("quit"),
    }
}
```

| Syntax | What it does |
| --- | --- |
| Option: Some(v) / None | A value that may be missing; Rust has no null |
| if let Some(p) = maybe { } | Match one pattern |
| if let Some(p) = maybe && p < 1024 { } | let chains (edition 2024, Rust 1.88+) |
| let Some(p) = maybe else { return; }; | let–else: bind or leave early |
| opt.unwrap_or(0), opt.map(|p| p + 1) | Default value / transform the inner value |
| #[derive(Debug, Clone, PartialEq)] | Generate common trait implementations |

## Collections and iterators

| Syntax | What it does |
| --- | --- |
| let mut v = vec![5, 3, 8]; | Growable vector |
| v.push(1), v.pop(), v.len() | Add, remove last, length |
| v[0] / v.get(10) | Index (panics if out of range) / returns Option |
| v.sort(), v.sort_by_key(|s| s.port) | Sort in place |
| v.retain(|&n| n != 3) | Keep only matching items |
| v.iter().map(|n| n * n).filter(|n| *n > 1).collect::() | Lazy iterator chain, collected into a new Vec |
| v.iter().sum::(), .count(), .max() | Common consumers |
| for (i, x) in v.iter().enumerate() | Index and value |
| HashMap::new() + m.insert(k, v) + m.get(&k) | Hash map from std::collections |
| *m.entry(k).or_insert(0) += 1; | Counting with the entry API |

## Error handling with Result and ?

```
use std::{error::Error, fs};

fn parse_port(s: &str) -> Result {
    let p: u16 = s.trim().parse()?;     // ? returns the error early
    Ok(p)
}

fn run() -> Result {
    let text = fs::read_to_string("/etc/hostname")?;
    println!("{}", text.trim());
    Ok(())
}

fn main() {
    if let Err(e) = run() {
        eprintln!("error: {e}");
        std::process::exit(1);
    }
}
```

| Syntax | Use it for |
| --- | --- |
| Result: Ok(v) / Err(e) | Any operation that can fail |
| ? | Return the error to the caller, converting it with From if needed |
| Box | Any error type; fine for binaries and scripts |
| Custom enum + impl Display + impl Error | Typed errors for libraries |
| .unwrap() / .expect("msg") | Panic on error; acceptable in tests and prototypes |
| .ok_or(err) | Turn an Option into a Result |
| panic!("...") | Unrecoverable bug; unwinds the thread |

## Traits, generics and threads

```
trait Check {
    fn name(&self) -> String;
    fn healthy(&self) -> bool { true }   // default method
}

impl Check for Server {
    fn name(&self) -> String { format!("{}:{}", self.name, self.port) }
}

fn report(c: &impl Check) -> String { /* static dispatch */ c.name() }
fn largest(items: &[T]) -> T {   // trait bounds
    items.iter().copied().fold(items[0], |m, x| if x > m { x } else { m })
}
let checks: Vec = vec![Box::new(srv)];  // dynamic dispatch
```

| Syntax | What it does |
| --- | --- |
| thread::spawn(move || { ... }) | New OS thread; move transfers captured values |
| handle.join().unwrap() | Wait for a thread |
| thread::scope(|s| { s.spawn(|| ...); }) | Threads that may borrow local data |
| Arc::new(Mutex::new(0)) | Shared, mutable state across threads |
| *c.lock().unwrap() += 1; | Lock, modify, unlock at end of statement |
| let (tx, rx) = mpsc::channel(); | Message passing between threads |

The standard library has no async runtime. For async network services, most projects add a runtime crate such as Tokio with `cargo add`.

## Files, arguments and a log example

For server admins: count failed SSH logins per source IP, reading the log line by line so large files stay cheap.

```
use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io::{self, BufRead, BufReader};

fn main() -> io::Result {
    let path = env::args().nth(1).unwrap_or_else(|| "/var/log/secure".into());
    let reader = BufReader::new(File::open(&path)?);

    let mut hits: HashMap = HashMap::new();
    for line in reader.lines() {
        let line = line?;
        if let Some(rest) = line.split("Failed password").nth(1)
            && let Some(ip) = rest.split(" from ").nth(1)
        {
            let ip = ip.split_whitespace().next().unwrap_or("?");
            *hits.entry(ip.to_string()).or_insert(0) += 1;
        }
    }
    let mut top: Vec = hits.into_iter().collect();
    top.sort_by_key(|(_, n)| std::cmp::Reverse(*n));
    for (ip, n) in top.iter().take(10) {
        println!("{n:>7} {ip}");
    }
    Ok(())
}
```

| Syntax | What it does |
| --- | --- |
| fs::read_to_string(path)? | Whole file as a String |
| fs::write(path, data)? | Create or overwrite a file |
| env::args().nth(1) | First command-line argument |
| env::var("HOME") | Environment variable as Result |
| Command::new("systemctl").args(["is-active", "nginx"]).output()? | Run a command without a shell (std::process::Command) |
| #[cfg(test)] mod tests { #[test] fn t() { assert_eq!(2 + 2, 4); } } | Unit test, run by cargo test |

**Official documentation (checked 9 October 2026):** [The Rust Programming Language (the book)](https://doc.rust-lang.org/book/) · [Rust standard library](https://doc.rust-lang.org/std/) · [The Rust Reference](https://doc.rust-lang.org/reference/) · [The Cargo Book](https://doc.rust-lang.org/cargo/) · [Edition Guide: Rust 2024](https://doc.rust-lang.org/edition-guide/rust-2024/index.html) · [Rust Release Notes](https://doc.rust-lang.org/stable/releases.html)

## Rust Cheat Sheet at a glance

In short: A compact reference for current stable Rust covering Cargo, ownership, pattern matching, collections, error handling, traits and threads.

Rust Cheat Sheet: what this guide covers.

**Related guides:** [C Cheat Sheet: C17 and C23 Syntax, Memory and gcc Flags](https://srvscripts.com/guides/c-cheat-sheet/) · [C++ Cheat Sheet: C++20 and C++23 Syntax, STL and Ranges](https://srvscripts.com/guides/cpp-cheat-sheet/) · [Go Cheat Sheet: Syntax, Slices, Errors and Goroutines](https://srvscripts.com/guides/go-cheat-sheet/).
