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 update to get new releases or rustup uninstall to remove Rust entirely. Think of it the way you’d think of nvm for Node.js or pyenv for Python.
  • rustc is the actual compiler. It takes a .rs source file and turns it into a native executable. You will rarely call rustc directly 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 rustc with 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 --version and cargo --version right after installing to confirm both are on your PATH before writing any code.
  • Use cargo new for every project, even tiny experiments — it gives you dependency management, a standard layout, and cargo test/cargo run for free, instead of juggling loose .rs files and manual rustc calls.
  • Install the extra components most projects want: rustup component add clippy rustfmt gives 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 update to 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 --version and cargo --version and confirm you see version numbers rather than a "command not found" error.
  • Create a new project named greeter with cargo new greeter, edit src/main.rs so it prints a greeting using your own name in a println! format string, and run it with cargo run. Expected output: a line containing your name.
  • Run rustup component add clippy, then run cargo clippy inside your greeter project. 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), and cargo (build tool, package manager, and test runner).
  • Install with the official rustup script on macOS/Linux or rustup-init.exe on Windows — avoid OS package managers, which lag behind current stable.
  • Verify the install with rustc --version and cargo --version; if either isn’t found, reload your shell so the updated PATH takes effect.
  • You can compile a single file directly with rustc file.rs, but real projects should use cargo new and cargo run from the start.
  • A missing ! on println! is a classic first-day compile error — the compiler’s suggestion will point you straight to the fix.
  • Add clippy and rustfmt with rustup component add clippy rustfmt, and keep everything current with rustup update.