Installing Rust
Before you can write a single line of Rust, you need the Rust toolchain on your machine: the rustc compiler, the cargo build tool and package manager, and the standard library. The official and strongly recommended way to get all three is a small program called rustup, which installs Rust, keeps it updated, and lets you switch between versions. This lesson walks through installing Rust on Windows, macOS, and Linux, verifying the install, and compiling your very first programs.
Overview: rustup, rustc, and Cargo
Rust is not one program but a small toolchain made of three pieces that work together, and understanding what each one does will save you confusion later:
- rustup is the toolchain installer and version manager. It downloads and installs Rust, and later lets you run
rustup updateto get new releases orrustup uninstallto remove Rust entirely. Think of it the way you’d think ofnvmfor Node.js orpyenvfor Python. - rustc is the actual compiler. It takes a
.rssource file and turns it into a native executable. You will rarely callrustcdirectly once you start real projects, but it is worth using once so you understand what Cargo is doing under the hood. - cargo is Rust’s build tool, package manager, and test runner, all in one. It creates new projects with a standard layout, downloads and compiles dependencies (called crates) from the crates.io registry, and wraps
rustcwith sensible defaults so you almost never invoke the compiler by hand.
Why does Rust route installation through rustup instead of a plain download or your operating system’s package manager? Two reasons matter. First, Rust ships a new stable release every six weeks, and rustup makes staying current a single command. Second, Rust supports multiple toolchains (stable, beta, nightly) and multiple compilation targets (for example, compiling for a Raspberry Pi from your laptop), and rustup manages all of that without touching your system Python or system compiler. Operating-system package managers like apt or yum often ship an old, frozen version of Rust that lags months or years behind, which is why this lesson only covers rustup.
Installing Rust
macOS and Linux
Open a terminal and run the official install script, which downloads rustup and walks you through the setup interactively:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
The script explains what it is about to do before it does anything, then asks you to press 1 to proceed with the default installation (this is almost always what you want). It installs rustc, cargo, rustup itself, and documentation into ~/.cargo and ~/.rustup, and it adds ~/.cargo/bin to your PATH by editing your shell’s profile file. On Linux you also need a linker and a C compiler (usually already present, but on a fresh system run sudo apt install build-essential on Debian/Ubuntu, or the equivalent for your distribution, before installing Rust).
Windows
Download and run rustup-init.exe from the official rustup website. Rust on Windows needs the Microsoft C++ build tools as a linker; the installer will detect if they’re missing and offer to install the "Desktop development with C++" workload from Visual Studio for you (you can accept the default option unless you already have MSVC or prefer the GNU toolchain instead). Once it finishes, open a new Command Prompt, PowerShell, or Windows Terminal window so your PATH picks up the newly installed tools.
Verifying the install
In any terminal, check that both tools are on your PATH and see which versions you have:
$ rustc --version\nrustc 1.79.0 (129f3b996 2024-06-10)\n$ cargo --version\ncargo 1.79.0 (ffa9cf99a 2024-06-03)
Your exact version numbers will differ depending on when you install, since Rust releases every six weeks — that’s expected and fine. If either command prints "command not found" (or "is not recognized" on Windows), the install script did not finish successfully or your terminal was opened before the PATH change took effect; see Common Mistakes below.
Examples
Example 1: Compiling a single file directly with rustc
Create a file named hello.rs with the following contents:
fn main() {\n println!("Hello, world!");\n}
Compile it with the raw compiler, then run the resulting executable:
rustc hello.rs\n./hello
Output:
Hello, world!
Here, rustc read hello.rs, checked it for type errors and (for more complex programs) ownership/borrowing violations, and produced a native binary called hello (or hello.exe on Windows) in the same directory. fn main() defines the entry point every Rust executable needs, and println! is a macro — note the !, which is how Rust distinguishes macro calls from ordinary function calls — that formats and prints text followed by a newline.
Example 2: Creating and running a project with Cargo
For anything beyond a single throwaway file, use Cargo instead of calling rustc yourself. Create a new project, then build and run it in one step:
cargo new hello_cargo\ncd hello_cargo\ncargo run
Output:
Compiling hello_cargo v0.1.0 (/home/you/hello_cargo)\n Finished dev [unoptimized + debuginfo] target(s) in 0.32s\n Running `target/debug/hello_cargo`\nHello, world!
cargo new scaffolds a directory containing a Cargo.toml manifest (project name, version, and dependencies) and a src/main.rs file already populated with a working program identical to the one below. cargo run compiles the project (placing the binary under target/debug/) and immediately executes it — one command instead of two:
fn main() {\n println!("Hello, world!");\n}
Output:
Hello, world!
Example 3: A slightly more realistic first program
Let’s go one step further than the template and print something computed rather than a fixed string, to confirm your toolchain handles variables and formatted output correctly:
fn main() {\n let name = "Ferris";\n let language = "Rust";\n let version = 1;\n\n println!("Hello, {}! You just compiled your first {} program (attempt #{}).", name, language, version);\n}
Output:
Hello, Ferris! You just compiled your first Rust program (attempt #1).
Each {} in the format string is a placeholder that println! fills in, in order, with the arguments that follow — name, then language, then version. This is the same formatting machinery you’ll use constantly throughout the rest of the course.
How It Works Step by Step
When you run rustc hello.rs (or cargo build, which calls rustc for you with the right flags), several stages happen in sequence:
- Lexing and parsing — the source text is tokenized and parsed into an abstract syntax tree, catching plain syntax errors like a missing brace.
- Type checking and borrow checking — the compiler infers and checks every type, and separately verifies that ownership and borrowing rules are respected (no use-after-move, no conflicting mutable/immutable borrows). This is the stage that makes Rust’s compiler feel strict compared to C or Python — it rejects entire classes of memory bugs before your program ever runs.
- Code generation — the checked program is lowered to LLVM intermediate representation and then to native machine code for your platform.
- Linking — the generated object code is linked against the Rust standard library and system libraries (this is why Windows needs the MSVC linker, and Linux needs a C toolchain) to produce a single executable.
Cargo adds a layer on top of this: when you run cargo run or cargo build, Cargo first reads Cargo.toml, resolves and (if needed) downloads any dependencies into ~/.cargo/registry, then invokes rustc for your crate and every dependency, caching the results under target/ so unchanged code isn’t recompiled next time.
Common Mistakes
Mistake 1: Opening a terminal before the PATH change takes effect
Right after running the install script, some terminals (especially ones already open) don’t pick up the new PATH entry, so cargo or rustc appears "not found" even though the install succeeded. On macOS and Linux, either open a brand-new terminal window or manually load the environment file for your current session:
source "$HOME/.cargo/env"
On Windows, simply closing and reopening your terminal application is enough, since the installer updates the persistent environment variables used by new processes.
Mistake 2: Installing Rust through the OS package manager
Running something like sudo apt install rustc will get you a Rust compiler, but often one that is many releases behind current stable, sometimes missing edition features or standard-library additions this course relies on. Worse, mixing an apt-installed Rust with a later rustup-installed Rust on the same PATH leads to confusing "which rustc is actually running" bugs. Always prefer rustup, and if you already installed via a package manager, remove it (sudo apt remove rustc cargo) before installing rustup so there’s exactly one toolchain on your system.
Mistake 3: Forgetting the ! on println!
Because println! looks like an ordinary function call, beginners often drop the !:
fn main() {\n println("Hello, world!");\n}
This fails to compile, because println (without !) is not a function in the standard library at all — only the macro println! exists. The compiler reports something like cannot find function `println` in this scope and, helpfully, suggests the fix. The corrected line simply adds the ! back:
println!("Hello, world!");
Keep an eye out for this any time you see an "unresolved name" error on something that looks like a normal function call — check whether it’s actually meant to be a macro.
Best Practices
- Always install through rustup rather than a system package manager, so you get current releases and easy upgrades via
rustup update. - Run
rustc --versionandcargo --versionright after installing to confirm both are on yourPATHbefore writing any code. - Use
cargo newfor every project, even tiny experiments — it gives you dependency management, a standard layout, andcargo test/cargo runfor free, instead of juggling loose.rsfiles and manualrustccalls. - Install the extra components most projects want:
rustup component add clippy rustfmtgives you a linter (cargo clippy) and an auto-formatter (cargo fmt). - Install an editor integration — the rust-analyzer extension (available for VS Code and other editors) gives you inline type information, autocompletion, and borrow-checker errors as you type, which is invaluable while you’re still building intuition for ownership.
- Periodically run
rustup updateto stay on the latest stable release; Rust’s stability guarantees mean this almost never breaks existing code.
Practice Exercises
- Install Rust with rustup if you haven’t already, then run
rustc --versionandcargo --versionand confirm you see version numbers rather than a "command not found" error. - Create a new project named
greeterwithcargo new greeter, editsrc/main.rsso it prints a greeting using your own name in aprintln!format string, and run it withcargo run. Expected output: a line containing your name. - Run
rustup component add clippy, then runcargo clippyinside yourgreeterproject. Read through any suggestions clippy prints, even though a program this small likely has none.
Summary
- Rust’s toolchain has three parts:
rustup(installer/version manager),rustc(the compiler), andcargo(build tool, package manager, and test runner). - Install with the official rustup script on macOS/Linux or
rustup-init.exeon Windows — avoid OS package managers, which lag behind current stable. - Verify the install with
rustc --versionandcargo --version; if either isn’t found, reload your shell so the updatedPATHtakes effect. - You can compile a single file directly with
rustc file.rs, but real projects should usecargo newandcargo runfrom the start. - A missing
!onprintln!is a classic first-day compile error — the compiler’s suggestion will point you straight to the fix. - Add
clippyandrustfmtwithrustup component add clippy rustfmt, and keep everything current withrustup update.
