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