01 · Setup & First Program¶
🎥 Video walkthrough¶
Rust programs are compiled ahead of time into native machine code by rustc,
the Rust compiler. In practice you rarely invoke rustc directly — you use
Cargo, Rust's build tool and package manager, which wraps compilation,
dependency management, and project scaffolding into one command-line tool.
Both are installed together via rustup, the official toolchain installer.
Install rustup¶
# macOS / Linux
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Windows: download and run rustup-init.exe from https://rustup.rs
# (it installs the same toolchain plus the MSVC build tools it needs)
The installer downloads the compiler (rustc), the package manager (cargo),
and a few companion tools, then adds them to your shell's PATH. Restart your
terminal (or run the source command the installer prints) so the new PATH
takes effect.
Verify the install:
rustc --version
# rustc 1.8x.0 (xxxxxxxxx 2026-xx-xx)
cargo --version
# cargo 1.8x.0 (xxxxxxxxx 2026-xx-xx)
rustc compiles a single source file into an executable; cargo manages
entire projects — compiling, running, testing, formatting, and pulling in
third-party libraries (called crates). From here on, we'll use Cargo almost
exclusively.
Creating a project with Cargo¶
This creates a new directory called hello_rust with everything a minimal
project needs already in place:
Cargo.toml is the project's manifest — metadata and dependencies, written in
TOML:
src/main.rs is the entry-point source file, and Cargo has already filled it
in with a working program:
cargo new also initializes a git repository and a .gitignore (which
excludes the target/ build output directory) unless you're already inside
one.
Running the program¶
From inside the project directory:
Compiling hello_rust v0.1.0 (/path/to/hello_rust)
Finished dev [unoptimized + debuginfo] target(s) in 0.42s
Running `target/debug/hello_rust`
Hello, world!
cargo run compiles the project (if anything changed since the last build)
and then immediately runs the resulting binary. It's the command you'll reach
for constantly during development.
cargo build vs cargo run vs cargo build --release¶
cargo build
# Compiles only -- produces target/debug/hello_rust, does NOT run it.
cargo build --release
# Compiles with optimizations turned on -- produces target/release/hello_rust.
# Release builds are slower to compile but much faster to run; use them for
# anything you'd actually ship or benchmark.
| Command | Compiles? | Runs? | Output location | Optimized? |
|---|---|---|---|---|
cargo build |
Yes (if needed) | No | target/debug/ |
No |
cargo run |
Yes (if needed) | Yes | target/debug/ |
No |
cargo build --release |
Yes (if needed) | No | target/release/ |
Yes |
During day-to-day learning and small projects, cargo run is all you need.
Reach for --release once you care about runtime speed.
Anatomy of the program¶
| Piece | Meaning |
|---|---|
fn main() |
The program's entry point — Cargo always looks for a main function in src/main.rs. |
println!("Hello, world!") |
Prints text followed by a newline. The trailing ! marks this as a macro call, not a plain function call. |
; |
Every statement ends with a semicolon. |
{ } |
Curly braces delimit blocks — function bodies, loop bodies, and more. |
The ! after println isn't decoration — println! is a macro that expands
at compile time to generate the actual formatting and printing code. You'll
meet other macros like vec! and format! soon; anything ending in ! is a
macro invocation, not a regular function.
Choosing an editor¶
VS Code with the rust-analyzer extension is the standard choice for
Rust development — it's free, and rust-analyzer gives you inline type
hints, autocomplete, jump-to-definition, and real-time error checking that
matches what the compiler itself would say. Install VS Code, then install
"rust-analyzer" from the Extensions panel, and it will auto-detect any Cargo
project you open. (RustRover from JetBrains is a solid paid alternative if
you prefer a full IDE.)
How It Actually Works¶
rustc is a single, ahead-of-time compiler pipeline — no separate linker
step you have to think about, no JIT, no VM to ship alongside the binary.
Source goes through several intermediate representations before machine code:
Rust source → AST → HIR (High-level IR, roughly desugared Rust) →
MIR (Mid-level IR, where borrow-checking actually happens) → LLVM IR →
native machine code via LLVM's backend. This is why cargo build feels slow
compared to an interpreted language starting up: the compiler is doing full
type inference, trait resolution, borrow checking, and LLVM optimization
passes before you get an executable, and why the payoff is a binary with no
runtime interpreter overhead at all.
cargo build and cargo run skip recompilation of unchanged code by
checking file hashes and dependency graphs recorded in target/debug/.fingerprint/
and Cargo.lock — this incremental compilation cache is why the second
cargo run after a one-line edit is dramatically faster than the first.
--release re-runs LLVM with optimization level 3 and strips debug
assertions (like integer-overflow panics), trading longer compile times for
a binary that can be 10-30x faster at runtime — the difference matters
because Rust's whole value proposition is compiling abstractions down to
code as tight as hand-written C, and --release is the only build mode
where that promise actually gets cashed in.
🔀 See this in another language¶
Exercise¶
Use cargo new greeter to scaffold a new project. Edit src/main.rs so
main prints a greeting for three different names, one println! call per
name. Run it with cargo run, then run cargo build --release and confirm
the optimized binary exists at target/release/greeter.