Rust Cheatsheet

Modules

Use this Rust reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

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");