Installing Rust and Running Your First Program
Cargo — the one tool that handles every part of a Rust project
Most languages hand you a compiler and wish you luck. You’re left to find a build system, a package manager, a test runner, and a way to glue them together — a weekend lost before you write a line of real code.
Rust hands you one command instead, and it already knows how to do all of that.
Before you can do anything else with Rust, you need a working installation and a way to turn your source code — the text you wrote — into a program the computer can actually run. In Rust, one tool, Cargo, handles this for every project, large or small. Learn Cargo first and every later lesson becomes just “what code do I put in this file?” Cargo handles the rest.
What’s actually happening when you “run a program”
Your source code is just text. A .rs file is no different from a .txt file as far as your computer is concerned — until you ask a compiler to read it. The compiler is itself a program: it reads your text, checks that it makes sense (this is where Rust catches its famous borrow-checker errors), and writes out a new file called a binary or executable. That file is what your computer can run directly.
So “running a Rust program” is really two steps:
- Compile: text source code → binary file. The Rust compiler is named
rustc. You almost never call it directly; you let Cargo do it. - Execute: run the binary. The operating system loads it into memory and starts following its instructions.
Cargo combines these two steps for you and adds a great deal more on top.
A few words you’ll need
- Package: a self-contained Rust project — one directory with one settings file and your source code.
- Crate: the unit of code Rust knows how to compile. A package contains one or more crates. Most of the time, package and crate refer to the same thing; the distinction matters mostly for large projects.
- Dependency: another package you want to use in your own project. Cargo downloads them automatically.
- Manifest: a settings file, named
Cargo.toml, that describes your project — its name, version, edition, and dependencies. - Registry: the public library Cargo downloads dependencies from. Rust’s is crates.io.
Three tools, one job each
When you install Rust you actually get three programs, stacked on top of one another. Keeping them straight saves a lot of confusion later:
rustupis the toolchain installer. It installs and updates Rust itself — the compiler and the standard library — and lets you switch between versions. You run it once to get set up, then rarely again.rustcis the compiler. It takes one.rsfile and produces a binary. Powerful, but low-level: you’d have to list every source file and every dependency by hand.cargois the build tool and package manager that sits on top ofrustc. It reads your manifest, callsrustcwith the right arguments, fetches dependencies, runs tests, and builds documentation. This is the one you’ll actually type all day.
The mental model: rustup manages Rust, cargo manages your project, and rustc is the engine cargo drives so you don’t have to.
Installing Rust
On macOS, Linux, or Windows WSL, the official one-liner is:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
That installs rustup, which in turn installs rustc and cargo. After installation, restart your terminal and check:
rustc --version
cargo --version
Both should print version numbers. If they do, you’re ready.
Your first project
The code boxes on this page run real Rust in your browser, so you can follow along even before you’ve installed anything. We’ll start with the smallest program there is. Hit Run and watch a real compiler turn this text into output:
That is the exact contents of src/main.rs in a fresh project. On your own machine you don’t type that file by hand — Cargo scaffolds it. Pick a directory where you keep code, open a terminal there, and run:
cargo new hello
That creates a new directory called hello/ with this shape:
hello/
├── Cargo.toml ← manifest: name, version, edition, dependencies
├── .gitignore ← tells git to ignore the build folder
└── src/
└── main.rs ← your code starts here
cargo new also runs git init for you, so the project is a git repository from day one. Open src/main.rs and you’ll find the same three lines you just ran above. Now build and run it:
cd hello
cargo run
You’ll see something like:
Compiling hello v0.1.0 (/path/to/hello)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.42s
Running `target/debug/hello`
Hello, world!
What just happened:
- Cargo read
Cargo.tomlto learn the project’s name and settings. - It compiled
src/main.rsinto a binary attarget/debug/hello. - It ran that binary.
- The binary printed
Hello, world!.
You wrote one file; Cargo handled the rest.
What Cargo.toml looks like
Open the manifest:
[package]
name = "hello"
version = "0.1.0"
edition = "2024"
[dependencies]
Plain key-value settings under named sections ([package], [dependencies]). To use someone else’s code you add a line under [dependencies] — but you don’t have to edit the file by hand. There’s a command:
cargo add rand
That looks up the popular rand crate on crates.io, writes a compatible version into Cargo.toml, and downloads the source so the next build can use it. Once a crate is a dependency, you call its functions exactly like the standard library’s. Here rand gives us a random number with no setup of our own:
(The Playground keeps the most popular crates pre-loaded, which is why rand works here without a cargo add. On your own machine, that one command is the whole installation step.) Run it a few times — the number changes because the program asks the operating system for fresh randomness each run. Version pinning, the lockfile, fetching dependencies-of-dependencies — Cargo handles all of it automatically.
Editions: how Rust changes without breaking your code
You may have noticed edition = "2024" in the manifest. An edition is Rust’s mechanism for evolving the language without breaking the millions of lines of Rust already in the wild. Every few years a new edition (2015, 2018, 2021, 2024) can introduce new keywords or change defaults — things that would otherwise make old code stop compiling.
The trick: each crate declares its own edition in Cargo.toml, and the compiler honors it per crate. A brand-new crate on edition 2024 can depend on an ancient crate still on edition 2015, and both compile in the same build. Nobody is forced to upgrade, and the language is free to improve. You don’t have to think about editions yet — just know that the line in your manifest is what lets a five-year-old library keep working forever.
What an edition can and can't change
An edition can change things that are local to a crate’s own source: which words are reserved as keywords (async and await became keywords in 2018), what a bare extern crate means, how closures capture variables (2021 tightened this), and similar surface-syntax decisions. What an edition can not change is the standard library or the meaning of compiled code — every edition links against the same std and produces the same machine-level result. That’s the whole reason cross-edition dependencies work: the editions differ only in how the text is interpreted, not in what the binary does. The compiler effectively reads each crate in the dialect it declares, then lowers everything to one common representation before generating code. So upgrading an edition is mechanical and low-risk — cargo fix --edition does most of it automatically — and you never have to coordinate the upgrade with anyone else’s crate.
A program is just functions
Real projects are more than one println!. The unit you build with is the function: a named block of code that takes inputs and returns a result. Here greet takes a name and hands back a sentence; main calls it. This is the shape every Rust program grows into.
Change "world" to your own name and run it again. The signature fn greet(name: &str) -> String reads as “a function named greet that takes a piece of text and returns owned text” — you’ll meet &str and String properly in lesson 4. For now, notice that main stays tiny and the real work lives in a function it calls.
Watch the compiler stop a bug
Here is the experience that makes Rust Rust. The program below tries to store a piece of text in a variable that was promised to hold a whole number. That’s a contradiction, and Rust refuses to build it. Hit Run and read the error before continuing:
The compiler reports error[E0308]: mismatched types and points at the exact spot: expected u32, found &str. The program never ran and never produced a wrong answer at 3 a.m. in production — the mistake was caught at the door, with a message that names the problem and the line. In many languages this same slip survives to runtime. Rust shifts the failure earlier, to the cheapest possible place to fix it. Every cargo build runs this check first.
The two build modes
When you run cargo build (or cargo run), Cargo defaults to dev mode: compile quickly, run slowly, include extra debugging information. That’s what you want while writing code — fast iteration.
When you’re ready to share the program or measure its real speed, switch to release mode:
cargo build --release
Release mode tells the compiler “spend more time, produce faster code.” The difference is large — a tight numeric loop can be 10 to 100 times faster in release mode, at the cost of slower compilation. Here’s the kind of loop where it shows. It runs fine in the Playground’s debug build; on your machine, --release would make the same work finish far quicker:
The rule of thumb:
- Day-to-day coding: dev mode (the default).
- Benchmarking: release.
- Shipping to other people: release.
The output goes to a different folder too — target/release/ instead of target/debug/ — so you can keep both around.
A faster sanity check
When you just want to know “does my code make sense?” without producing a binary, use:
cargo check
This runs the compiler’s full analysis — all the type-checking and borrow-checking — but stops before generating the binary. It’s much faster than cargo build, and you’ll run it constantly: edit, cargo check, fix the errors, repeat, and only cargo run when it’s clean.
The short history of the box
Bundler ships for Ruby. Its authors learn what a package manager needs to be trusted: a lockfile, semantic versioning, one registry.
Yehuda Katz and Carl Lerche begin Cargo for Rust, carrying Bundler's lessons over. crates.io opens as the central registry.
Rust 1.0 ships with Cargo built in — a rarity. Most languages bolt on a package manager years later; Rust had one on day one.
The first new edition after 2015 arrives, proving the language can evolve (new keywords, new defaults) without breaking existing crates.
The 2024 edition lands — the current default for cargo new — while crates on 2015 still compile untouched, side by side.
Key takeaways
- Source code is text; a compiler (
rustc) turns it into a runnable binary; the OS executes the binary. - Three tools stack up:
rustupmanages Rust,cargomanages your project,rustcis the engine Cargo drives. cargo new <name>scaffolds a project;cargo runbuilds and runs it;cargo checkis the fast type-check;cargo add <name>pulls a dependency from crates.io.- Use dev mode while writing, release mode (
--release) for benchmarking and shipping. Cargo.tomlis the manifest. Itseditionline lets new and old crates compile side by side, so the language can improve without breaking your code.
One command stands between you and a running program, and it already knows how to fetch the world’s libraries, build them, run your tests, and stop a type error before it ever reaches a user.
That’s the gift Cargo’s authors carried over from a decade of package-manager scar tissue. With it installed, the rest of this course is simple: edit src/main.rs, run cargo run, watch what happens. The next lesson fills that file with variables and types — the raw material every program is built from.