rustc-basics
rustc Basics
Purpose
Guide agents through Rust compiler invocation: RUSTFLAGS, Cargo profile configuration, build modes, MIR and assembly inspection, monomorphization, and common compilation error patterns.
Triggers
- "How do I configure a release build in Rust for maximum performance?"
- "How do I see the assembly output for a Rust function?"
- "What is monomorphization and why is it making my compile slow?"
- "How do I enable LTO in Rust?"
- "My Rust binary is too large — how do I shrink it?"
- "How do I read Rust MIR output?"
Workflow
1. Choose a build mode
# Debug (default) — fast compile, no optimization, debug info
cargo build
# Release — optimized, no debug info by default
cargo build --release
# Check only (fastest, no codegen)
cargo check
# Build for specific target
cargo build --release --target aarch64-unknown-linux-gnu
2. Cargo.toml profile configuration
[profile.release]
opt-level = 3 # 0-3, "s" (size), "z" (aggressive size)
debug = false # true = full, 1 = line tables only, 0 = none
lto = "thin" # false | "thin" | true (fat LTO)
codegen-units = 1 # 1 = max optimization, higher = faster compile
panic = "abort" # "unwind" (default) | "abort" (smaller binary)
strip = "symbols" # "none" | "debuginfo" | "symbols"
overflow-checks = false # default true in debug, false in release
[profile.release-with-debug]
inherits = "release"
debug = true # release build with debug symbols
strip = "none"
[profile.dev]
opt-level = 1 # Speed up debug builds slightly
| Setting | Impact |
|---|---|
lto = true (fat) |
Best optimization, slowest link |
lto = "thin" |
Good optimization, parallel link |
codegen-units = 1 |
Best inlining, slower compile |
panic = "abort" |
Removes unwind tables, smaller binary |
opt-level = "z" |
Aggressive size reduction |
3. RUSTFLAGS
# Set for a single build
RUSTFLAGS="-C target-cpu=native" cargo build --release
# Enable all target CPU features
RUSTFLAGS="-C target-cpu=native -C target-feature=+avx2,+bmi2" cargo build --release
# Control codegen at invocation level
RUSTFLAGS="-C opt-level=3 -C codegen-units=1 -C lto=on" cargo build --release
Persistent in .cargo/config.toml:
[build]
rustflags = ["-C", "target-cpu=native"]
[target.x86_64-unknown-linux-gnu]
rustflags = ["-C", "target-cpu=native", "-C", "link-arg=-fuse-ld=lld"]
4. Inspect assembly output
# Using cargo-show-asm (recommended)
cargo install cargo-show-asm
cargo asm --release 'myapp::module::function_name'
# Using rustc directly
rustc --emit=asm -C opt-level=3 -C target-cpu=native src/lib.rs
cat lib.s
# View MIR (mid-level IR, before codegen)
rustc --emit=mir -C opt-level=3 src/lib.rs
cat lib.mir
# View LLVM IR
rustc --emit=llvm-ir -C opt-level=3 src/lib.rs
cat lib.ll
# Use Compiler Explorer (Godbolt) patterns locally
RUSTFLAGS="--emit=asm" cargo build --release
find target/ -name "*.s"
5. Understand monomorphization
Rust generics are monomorphized — each concrete type instantiation produces separate code. This causes:
- Binary size bloat
- Longer compile times
- Potential i-cache pressure
# Measure monomorphization bloat
cargo install cargo-llvm-lines
cargo llvm-lines --release | head -30
# Shows: lines of LLVM IR per function (monomorphized copies visible)
Mitigation strategies:
// 1. Type erasure with dyn Trait (trades monomorphization for dispatch)
fn process(iter: &mut dyn Iterator<Item = i32>) { ... }
// 2. Non-generic inner function pattern
fn my_generic<T: AsRef<str>>(s: T) {
fn inner(s: &str) { /* actual work */ }
inner(s.as_ref()) // monomorphization only in thin wrapper
}
6. Binary size reduction
[profile.release]
opt-level = "z"
lto = true
codegen-units = 1
panic = "abort"
strip = "symbols"
# Check binary size breakdown
cargo install cargo-bloat
cargo bloat --release --crates # per-crate size
cargo bloat --release -n 20 # top 20 largest functions
# Compress executable (at cost of startup time)
upx --best --lzma target/release/myapp
7. Common error triage
| Error | Cause | Fix |
|---|---|---|
cannot find function in this scope |
Missing use or wrong module path |
Add use crate::module::fn_name |
the trait X is not implemented for Y |
Missing impl or wrong generic bound | Implement trait or adjust bounds |
lifetime may not live long enough |
Borrow checker lifetime issue | Add explicit lifetime annotations |
cannot borrow as mutable because also borrowed as immutable |
Aliasing violation | Restructure borrows to not overlap |
use of moved value |
Value used after move into closure or function |
Use .clone() or borrow instead |
mismatched types: expected &str found String |
String vs &str confusion | Use .as_str() or &my_string |
8. Useful rustc flags
# Show all enabled features at a given opt level
rustc -C opt-level=3 --print cfg
# List available targets
rustc --print target-list
# Show target-specific features
rustc --print target-features --target x86_64-unknown-linux-gnu
# Explain an error code
rustc --explain E0382
For RUSTFLAGS reference and Cargo profile patterns, see references/rustflags-profiles.md.
Related skills
- Use
skills/rust/cargo-workflowsfor workspace management and Cargo tooling - Use
skills/rust/rust-debuggingfor debugging Rust binaries with GDB/LLDB - Use
skills/rust/rust-profilingfor profiling and flamegraphs - Use
skills/rust/rust-sanitizers-mirifor memory safety validation
More from mohitmishra786/low-level-dev-skills
cmake
CMake build system skill for C/C++ projects. Use when writing or refactoring CMakeLists.txt, configuring out-of-source builds, selecting generators (Ninja, Make, VS), managing targets and dependencies with target_link_libraries, integrating external packages via find_package or FetchContent, enabling sanitizers, setting up toolchain files for cross-compilation, or exporting CMake packages. Activates on queries about CMakeLists.txt, cmake configure errors, target properties, install rules, CPack, or CMake presets.
580static-analysis
Static analysis skill for C/C++ codebases. Use when hardening code quality, triaging noisy builds, running clang-tidy, cppcheck, or scan-build, interpreting check categories, suppressing false positives, or integrating static analysis into CI. Activates on queries about clang-tidy checks, cppcheck, scan-build, compile_commands.json, code hardening, or static analysis warnings.
407llvm
LLVM IR and pass pipeline skill. Use when working directly with LLVM Intermediate Representation (IR), running opt passes, generating IR with llc, inspecting or writing LLVM IR for custom passes, or understanding how the LLVM backend lowers IR to assembly. Activates on queries about LLVM IR, opt, llc, llvm-dis, LLVM passes, IR transformations, or building LLVM-based tools.
361gdb
GDB debugger skill for C/C++ programs. Use when starting a GDB session, setting breakpoints, stepping through code, inspecting variables, debugging crashes, using reverse debugging (record/replay), remote debugging with gdbserver, or loading core dumps. Activates on queries about GDB commands, segfaults, hangs, watchpoints, conditional breakpoints, pretty-printers, Python GDB scripting, or multi-threaded debugging.
153linux-perf
Linux perf profiler skill for CPU performance analysis. Use when collecting sampling profiles with perf record, generating perf report, measuring hardware counters (cache misses, branch mispredicts, IPC), identifying hot functions, or feeding perf data into flamegraph tools. Activates on queries about perf, Linux performance counters, PMU events, off-CPU profiling, perf stat, perf annotate, or sampling-based profiling on Linux.
142core-dumps
Core dump analysis skill for production crash triage. Use when loading core files in GDB or LLDB, enabling core dump generation on Linux/macOS, mapping symbols with debuginfo or debuginfod, or extracting backtraces from crashes without re-running the program. Activates on queries about core files, ulimit, coredumpctl, debuginfod, crash triage, or analyzing segfaults from production binaries.
131