Rust Cheatsheet

Modules

Use this Rust reference while you build software engineering projects, review code, or refresh the syntax you reach for most.

Module Basics

Modules organize code into namespaces. Use mod to define a module.

// Inline module
mod math {
    pub fn add(a: i32, b: i32) -> i32 { a + b }
    pub fn subtract(a: i32, b: i32) -> i32 { a - b }

    // Nested module
    pub mod advanced {
        pub fn square(x: i32) -> i32 { x * x }
    }
}

fn main() {
    let sum = math::add(3, 4);
    let sq = math::advanced::square(5);
}

Visibility (pub)

By default everything is private — only accessible within the same module and its children.

mod outer {
    pub struct Public;
    struct Private;           // only accessible in `outer`

    pub mod inner {
        pub fn visible() {}
        fn hidden() {}

        // pub(super) — accessible to parent module only
        pub(super) fn to_parent() {}

        // pub(crate) — accessible anywhere in the crate
        pub(crate) fn crate_wide() {}
    }
}

// Visibility levels:
// private (default)   — current module and its descendants
// pub(self)           — same as private
// pub(super)          — parent module
// pub(crate)          — anywhere in the crate
// pub(in path)        — specific ancestor module
// pub                 — anywhere (public API)

Struct field visibility:

pub struct Config {
    pub host: String,          // public field
    port: u16,                 // private — external code can't access directly
    pub(crate) timeout: u64,   // crate-visible
}

impl Config {
    pub fn new(host: String, port: u16) -> Self {
        Config { host, port, timeout: 30 }
    }
    pub fn port(&self) -> u16 { self.port }  // getter for private field
}

File-Based Modules

The compiler looks for module code in files automatically:

src/
├── main.rs        (or lib.rs for a library)
├── math.rs        — defines `mod math` content
├── math/
│   └── mod.rs     — alternative: defines `mod math` content
├── utils.rs
└── utils/
    ├── mod.rs
    ├── string.rs  — `mod string` inside utils/mod.rs
    └── number.rs

In main.rs:

mod math;      // compiler finds src/math.rs or src/math/mod.rs
mod utils;

fn main() {
    let result = math::add(1, 2);
}

src/math.rs:

pub fn add(a: i32, b: i32) -> i32 { a + b }
pub mod trig;  // compiler finds src/math/trig.rs

Inline declaration in a non-root file (Rust 2018+):

// src/utils/mod.rs or src/utils.rs
pub mod string;   // finds src/utils/string.rs
pub mod number;   // finds src/utils/number.rs

use — Bringing Paths into Scope

use std::collections::HashMap;
use std::io::{self, Read, Write};   // import multiple from same path

// Rename with `as`
use std::fmt::Result as FmtResult;
use std::io::Result as IoResult;

// Glob import (use sparingly)
use std::collections::*;

// Re-export (make visible to users of this module)
pub use crate::math::add;
pub use std::collections::HashMap;  // re-export a dependency

fn main() {
    let mut m = HashMap::new();
    m.insert("key", 1);
}

Nested path syntax:

use std::{
    cmp::Ordering,
    collections::{HashMap, HashSet},
    fmt::{self, Display, Debug},
    io::{self, Read, Write, BufReader},
};

Absolute vs Relative Paths

// Absolute paths start with crate name or `crate`
crate::utils::format_name("alice");
std::collections::HashMap::new();

// Relative paths start from the current module
utils::format_name("alice");   // if utils is a sibling module

// super — parent module
mod child {
    use super::parent_fn;      // function in parent module
    pub fn call() { super::parent_fn(); }
}

// self — current module (sometimes needed in use statements)
use self::sibling_module::something;

Crates

A crate is the root compilation unit. Two types: - Binary crate: has a main() function, produces an executable. - Library crate: lib.rs as root, produces a library for other crates.

# Cargo.toml
[package]
name = "my_crate"
version = "0.1.0"

[lib]
name = "my_crate"       # name of the library crate (default: package name)
path = "src/lib.rs"     # default

[[bin]]
name = "my_tool"        # binary name
path = "src/main.rs"    # default

[[bin]]
name = "other_tool"
path = "src/bin/other.rs"

Accessing your library from binaries in the same package:

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

Workspaces

A workspace contains multiple packages sharing Cargo.lock and output dir.

# workspace root Cargo.toml
[workspace]
members = [
    "core",
    "cli",
    "api",
]
resolver = "2"   # use v2 feature resolver (recommended)
my_workspace/
├── Cargo.toml        (workspace manifest)
├── Cargo.lock        (shared)
├── core/
│   ├── Cargo.toml
│   └── src/lib.rs
├── cli/
│   ├── Cargo.toml
│   └── src/main.rs
└── api/
    ├── Cargo.toml
    └── src/lib.rs

cli/Cargo.toml — depend on a workspace sibling:

[dependencies]
core = { path = "../core" }
cargo build                  # build all
cargo build -p cli           # build specific package
cargo test -p core           # test specific package
cargo run -p cli             # run specific binary

Cargo.toml Dependencies

[dependencies]
serde = "1"                               # version requirement
serde = "^1.0.100"                       # compatible with 1.0.100+
serde = "~1.0.100"                       # patch updates only
serde = "=1.0.100"                       # exact version
serde = "*"                              # any version (avoid)

# With features
serde = { version = "1", features = ["derive"] }

# Optional dependency (activate via feature flag)
serde = { version = "1", optional = true }

# From git
rand = { git = "https://github.com/rust-random/rand" }
rand = { git = "...", rev = "abc123" }
rand = { git = "...", branch = "master" }
rand = { git = "...", tag = "v0.8.5" }

# From local path
mylib = { path = "../mylib" }

[dev-dependencies]            # only for tests and examples
criterion = "0.5"

[build-dependencies]          # only for build.rs
cc = "1"

[features]
default = ["serde"]           # active by default
serde = ["dep:serde"]         # feature named "serde" enabling dep:serde
full = ["serde", "async"]
async = ["tokio"]

Enabling features:

cargo build --features "serde async"
cargo build --all-features
cargo build --no-default-features
cargo build --no-default-features --features "serde"

The Prelude

Rust automatically imports a set of commonly used items — the prelude — into every module:

// These are always in scope without `use`:
// std::marker::{Copy, Send, Sized, Sync, Unpin}
// std::ops::{Drop, Fn, FnMut, FnOnce}
// std::mem::drop
// std::boxed::Box
// std::borrow::ToOwned
// std::clone::Clone
// std::cmp::{PartialEq, PartialOrd, Eq, Ord}
// std::convert::{AsRef, AsMut, Into, From}
// std::default::Default
// std::iter::{Iterator, Extend, IntoIterator, DoubleEndedIterator, ExactSizeIterator}
// std::option::Option::{self, Some, None}
// std::result::Result::{self, Ok, Err}
// std::string::{String, ToString}
// std::vec::Vec

extern crate (Old Syntax)

Pre-Rust-2018, explicit extern crate was required. Now only needed for crates with a different name:

// 2015 edition (avoid)
extern crate serde;

// 2018+ edition — just use in Cargo.toml and import with `use`:
use serde::{Serialize, Deserialize};

Module Organization Patterns

// lib.rs — public API surface, re-exports from internal modules
pub mod error;
pub mod config;

// Re-export for ergonomic import at crate root
pub use error::Error;
pub use config::Config;

// Internal module (pub(crate) to keep out of public API)
pub(crate) mod internal;

Flat module tree (small projects):

src/
├── lib.rs
├── error.rs
├── parser.rs
└── types.rs

Domain-grouped module tree (larger projects):

src/
├── lib.rs
├── auth/
│   ├── mod.rs
│   ├── jwt.rs
│   └── oauth.rs
├── db/
│   ├── mod.rs
│   ├── schema.rs
│   └── queries.rs
└── api/
    ├── mod.rs
    ├── routes.rs
    └── handlers.rs

Conditional Compilation

// Only compile this on Linux
#[cfg(target_os = "linux")]
fn linux_only() {}

// Only compile in debug builds
#[cfg(debug_assertions)]
fn debug_check() { println!("debug mode"); }

// Feature flag
#[cfg(feature = "async")]
pub mod async_impl;

// Negate condition
#[cfg(not(feature = "serde"))]
fn no_serde_fallback() {}

// Multiple conditions
#[cfg(all(target_os = "linux", feature = "serde"))]
fn linux_with_serde() {}

#[cfg(any(target_os = "macos", target_os = "linux"))]
fn unix_only() {}

// cfg! macro — runtime check (actually compile-time)
if cfg!(debug_assertions) {
    println!("debug build");
}

// Conditionally include a file
#[cfg(target_os = "windows")]
include!("windows_impl.rs");