Rust (2024 edition) Module 1 – Getting started: rustup, Cargo and compiler messages
Install Rust with rustup and pin a toolchain
Install Rust with rustup, check what it installed, choose stable, beta or nightly, and pin a project to one release, its components and targets.
What you will learn
- Install Rust with rustup and verify the rustc, cargo and rustup versions
- Explain the stable, beta and nightly channels and when each is needed
- Predict which toolchain rustup runs from a +toolchain argument, the environment, overrides, files and the default
- Configure a project's toolchain, components and targets with rust-toolchain.toml
Before you start
What you’ll learn · Before you start
What you will learn
- Install Rust with rustup and verify the rustc, cargo and rustup versions
- Explain the stable, beta and nightly channels and when each is needed
- Predict which toolchain rustup runs from a +toolchain argument, the environment, overrides, files and the default
- Configure a project's toolchain, components and targets with rust-toolchain.toml
Before you start
On this page
The computer these sessions ran on has three Rust toolchain: One installed release of Rust's tools for one host: rustc, Cargo and the standard library, plus components such as rustfmt and Clippy. rustup names toolchains such as stable-aarch64-apple-darwin or 1.99.0-aarch64-apple-darwin. installed side by side, and the same command,
rustc --version, answers 1.98.0 in one folder and 1.99.0 in another. Nothing is broken: rustup: The installer and manager of Rust toolchains. Its commands in ~/.cargo/bin, cargo and rustc among them, are proxies that choose a toolchain for each run and hand the command to that toolchain's own program., the installer, chooses
a toolchain for every command, and one small file in a project decides which.
This lesson installs Rust with rustup, reads what it installed, explains the stable, beta and nightly channels, and follows the fixed order in which rustup chooses a toolchain. Then you pin a project to Rust 1.99.0, with the component: A part of a toolchain that rustup adds or removes on its own, such as clippy, rustfmt, rust-docs, rust-src or rust-analyzer, or the standard library for one target. and target: The kind of system a program is built for, named by a triple such as aarch64-apple-darwin, x86_64-pc-windows-msvc or wasm32-wasip1. Building for a target needs that target's standard library. it needs, so that every computer that builds it uses the same compiler.
rustup installs Rust, and every tool comes from a toolchain
On macOS and Linux, the install page of rust-lang.org gives one command, which downloads rustup’s installer and runs
it: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh. On Windows you download and run rustup-init.exe
instead, and you may be asked to install the Visual Studio C++ build tools first. Either way, the installer puts its
commands in ~/.cargo/bin (%USERPROFILE%\.cargo\bin on Windows), tries to add that folder to your PATH, and keeps
the toolchains in ~/.rustup. Open a new terminal afterwards, because the PATH change may not reach the one you have
open.
Rust also needs a linker, the program that joins compiled pieces into one executable, and it uses the system’s. The
Rust book’s advice for linker errors is to install a C compiler, which typically includes one: on macOS run
xcode-select --install, on Linux install GCC or Clang (on Ubuntu, the build-essential package), and on Windows
accept the Visual Studio installer when it is offered.
What rustup installs is a toolchain: one release of rustc, cargo and the standard library
for your computer, plus extra tools as components. The default profile adds rust-docs, rustfmt and clippy to the
three essentials; the minimal profile installs only rustc, rust-std and cargo, which is why some installs have no
cargo fmt. Here is what this computer reports, run in its home folder:
from session import run
# In the home folder, where no toolchain file applies: what rustup has installed, and what runs by default.
run("""
rustup --version
rustup show
rustc --version
cargo --version
""") Output
$ rustup --version rustup 1.29.1 (d95a37b6a) info: This is the version for the rustup toolchain manager, not the rustc compiler. info: the currently active `rustc` version is `rustc 1.98.0 (88d9e12ae)` $ rustup show Default host: aarch64-apple-darwin rustup home: ~/.rustup installed toolchains -------------------- stable-aarch64-apple-darwin (active, default) nightly-aarch64-apple-darwin 1.99.0-aarch64-apple-darwin active toolchain ---------------- name: stable-aarch64-apple-darwin active because: it's the default toolchain installed targets: aarch64-apple-darwin $ rustc --version rustc 1.98.0 (88d9e12ae) $ cargo --version cargo 1.98.0 (797e8a9bc)
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 versions_session.py
Three toolchains are installed: stable, nightly and the exact release 1.99.0. The default is stable, and on this
computer stable is still 1.98.0, because rustup update has not been run on it since 1.99.0 came out. That is the first reason
to pin a project: “stable” means whatever stable you installed last. The commands themselves are not the compilers:
the cargo and rustc in ~/.cargo/bin are proxies of rustup, small programs that hand each
command to the real toolchain. Step through one command:
The drawing shows the whole scene; its 7 steps are listed below.
Every step as text
- You type a command. You type cargo build. The shell looks for cargo on its PATH and finds it in ~/.cargo/bin, the folder rustup added to the PATH when it was installed.
- A proxy, not cargo. The program in ~/.cargo/bin is a proxy: a small program of rustup that only decides which toolchain should run, and then starts that toolchain's own cargo.
- It looks for a choice. It checks, in order: a +toolchain argument, the RUSTUP_TOOLCHAIN variable, a directory override, a rust-toolchain.toml file, the default. Here the first three are absent.
- The file decides. It finds ~/pinned/rust-toolchain.toml, which names channel 1.99.0. That toolchain is installed, so nothing has to be downloaded.
- The real cargo runs. The proxy starts cargo from the 1.99.0 toolchain with the same arguments. That cargo runs rustc from the same toolchain, so the two always match.
- The output comes back. What the build prints comes straight back to your terminal. The proxy adds nothing to it.
- Elsewhere, the default. In a folder with no toolchain file above it, the same command runs the default toolchain instead: on the computer these sessions ran on, stable was still 1.98.0.
Checkpoint
Check: What rustup installs
Stable, beta and nightly: three channels for three needs
rustup installs toolchains from three release channel: A stream of Rust releases: stable, a new release every six weeks; beta, the next stable release while it is tested; and nightly, built every night with features that are not finished yet.. Each answers a different need:
| Channel | A new build | Use it for |
|---|---|---|
| stable | every six weeks, with an occasional point release | all your work, and everything in this track |
| beta | the next stable release, tested before it ships | checking that your project builds on the coming release |
| nightly | every night | unfinished features and tools that need them, such as Miri |
A toolchain can be named by its channel (stable, beta, nightly) or by an exact release (1.99.0), and a
+toolchain argument picks one for a single command. Here the three installed toolchains answer one after the other,
and then the stable 1.99.0 and the nightly compile a file that uses an unfinished feature, try blocks:
// try blocks are an unfinished feature: only a nightly compiler accepts the #![feature] line.
#![feature(try_blocks)]
fn main() {
let total: Option<u32> = try { "40".parse::<u32>().ok()? + "25".parse::<u32>().ok()? };
println!("{total:?}");
} from session import run
# One computer, three toolchains: a +toolchain argument picks one for a single command.
run("""
rustc +stable --version
rustc +1.99.0 --version
rustc +nightly --version
!rustc +1.99.0 --edition 2024 gated.rs
rustc +nightly --edition 2024 gated.rs
./gated
""") Output
$ rustc +stable --version rustc 1.98.0 (88d9e12ae) $ rustc +1.99.0 --version rustc 1.99.0 (b940084d7) $ rustc +nightly --version rustc 1.101.0-nightly (0abfedbc7) $ rustc +1.99.0 --edition 2024 gated.rs error[E0554]: `#![feature]` may not be used on the stable release channel --> gated.rs:2:1 | 2 | #![feature(try_blocks)] | ^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0554`. [exit status 1] $ rustc +nightly --edition 2024 gated.rs $ ./gated Some(65)
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 channels_session.py
Stable Rust refused the #![feature(try_blocks)] line with error E0554: feature flags “may not be used on the stable
release channel”. The nightly compiler accepted it, and the program printed Some(65). This is the rule the Rust book
states: to try a feature that is still being worked on, you must use a nightly release and opt in with a flag in your
code, while on stable and beta no feature flags are allowed.
Switching everything to nightly for one feature
A whole project on nightly gets a new compiler every night, and an unfinished feature can still change or be
removed. Keep stable as the default and use cargo +nightly … for the one command that needs it, or pin one
nightly toolchain in that project’s toolchain file.
Checkpoint
Check: Stable, beta and nightly
rustup chooses the toolchain in a fixed order
Each time you run cargo, rustc or another of its commands, rustup looks for a choice in this order and uses the
first one it finds, as its book’s chapter on overrides describes:
- a
+toolchainargument on the command line, such ascargo +beta test; - the
RUSTUP_TOOLCHAINenvironment variable; - a directory override, set with
rustup override setfor a folder and the folders inside it; - a
rust-toolchain.tomlfile in the current folder or one above it; - the default toolchain, set with
rustup default.
Step through four commands and watch each one stop at the first stage that names a toolchain:
The drawing shows the whole scene; its 5 steps are listed below.
Every step as text
- rustc --version in ~/pinned. No +toolchain, no variable and no override, so rustup reaches the toolchain file in ~/pinned. It names 1.99.0, and rustc 1.99.0 answers.
- rustc +stable --version. The argument +stable comes first in the order, so it wins over the file in the same folder: stable, 1.98.0 on this computer, answers.
- With RUSTUP_TOOLCHAIN=nightly. Without a + argument, the variable is next. It names nightly, so the nightly toolchain answers, again before the file is read.
- rustc --version in ~. In the home folder there is no override and no toolchain file in it or above it, so rustup falls through to the default: stable.
- The first one found wins. A nearer directory override or toolchain file also wins over one further up. Whatever rustup chooses, rustup show active-toolchain says which one and why.
These are the same four commands, run in this computer’s home folder and in ~/pinned, a folder that holds a toolchain
file:
from session import run
# The same question in two folders, and two ways to overrule the toolchain file for one command.
run("""
rustup show active-toolchain
cd pinned
rustup show active-toolchain
rustc --version
rustc +stable --version
RUSTUP_TOOLCHAIN=nightly rustc --version
rustup override list
""") Output
$ rustup show active-toolchain stable-aarch64-apple-darwin (default) $ cd pinned $ rustup show active-toolchain 1.99.0-aarch64-apple-darwin (overridden by '~/pinned/rust-toolchain.toml') $ rustc --version rustc 1.99.0 (b940084d7) $ rustc +stable --version rustc 1.98.0 (88d9e12ae) $ RUSTUP_TOOLCHAIN=nightly rustc --version rustc 1.101.0-nightly (0abfedbc7) $ rustup override list no overrides
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 which_session.py
rustup show active-toolchain names the toolchain and the reason, which makes it the first command to run when a build
behaves differently on two computers. In the home folder the default answered; in ~/pinned the file did, and both
+stable and the environment variable overruled the file for one command. rustup override list found no directory
overrides. An override is kept in rustup’s own settings on one computer, so it is handy for an experiment, while a
toolchain file lives in the project and travels with it to every computer and to CI.
Key idea
rustup uses the first choice it finds: a +toolchain argument, then RUSTUP_TOOLCHAIN, a directory override, a rust-toolchain.toml file, and finally the default. rustup show active-toolchain tells you which one won.
Checkpoint
Check: How rustup chooses a toolchain
A rust-toolchain.toml pins the release, its components and its targets
A project is pinned by one file in its top folder. This one names the exact release, two components that every build of the project needs, and one extra target, WebAssembly with WASI, for which the standard library must be installed:
[toolchain]
channel = "1.99.0"
components = ["clippy", "rustfmt"]
targets = ["wasm32-wasip1"] Commit the file with the code. Any computer that builds the project then uses Rust 1.99.0, and if that toolchain is not
installed yet, rustup installs it on the first command, because automatic installation is on by default
(RUSTUP_AUTO_INSTALL=1). Here is what the file selects in ~/pinned:
from session import run
# What the pinned folder's toolchain holds: the file, the toolchain it selects, its components and targets.
run("""
cd pinned
cat rust-toolchain.toml
rustup show
rustup component list --installed
!rustup doc --path
""") Output
$ cd pinned $ cat rust-toolchain.toml [toolchain] channel = "1.99.0" components = ["clippy", "rustfmt"] targets = ["wasm32-wasip1"] $ rustup show Default host: aarch64-apple-darwin rustup home: ~/.rustup installed toolchains -------------------- stable-aarch64-apple-darwin (default) nightly-aarch64-apple-darwin 1.99.0-aarch64-apple-darwin (active) active toolchain ---------------- name: 1.99.0-aarch64-apple-darwin active because: overridden by '~/pinned/rust-toolchain.toml' installed targets: aarch64-apple-darwin thumbv7m-none-eabi wasm32-unknown-unknown wasm32-wasip1 wasm32-wasip2 $ rustup component list --installed cargo-aarch64-apple-darwin clippy-aarch64-apple-darwin rust-src rust-std-aarch64-apple-darwin rust-std-thumbv7m-none-eabi rust-std-wasm32-unknown-unknown rust-std-wasm32-wasip1 rust-std-wasm32-wasip2 rustc-aarch64-apple-darwin rustfmt-aarch64-apple-darwin $ rustup doc --path info: `rust-docs` not installed in toolchain `1.99.0-aarch64-apple-darwin` help: run `rustup component add --toolchain 1.99.0-aarch64-apple-darwin rust-docs` to install it error: unable to view documentation which is not installed [exit status 1]
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 pinned_session.py
The active toolchain is 1.99.0, “overridden by” the file. Its components include clippy and rustfmt, which the file
asks for, and rust-src, the standard library’s source code, but not rust-docs, so rustup doc cannot open the
offline documentation until rustup component add rust-docs installs it: the help line says exactly that. The
installed targets are the host, aarch64-apple-darwin, plus four that were added to this toolchain, wasm32-wasip1
among them.
A target is named by a triple such as aarch64-apple-darwin: the processor, the vendor and the operating system. Rust’s
platform support page sorts targets into tiers by what the project promises for them:
- Tier 1 with host tools, “guaranteed to work” and tested on every change: eight targets, among them
aarch64-apple-darwin,x86_64-unknown-linux-gnuandx86_64-pc-windows-msvc. - Tier 2 with host tools, “guaranteed to build”, with the compiler itself available there: among others
x86_64-apple-darwin, Macs with Intel processors. - Tier 2, “guaranteed to build”, for compiling from another computer:
wasm32-wasip1for WebAssembly andthumbv7m-none-eabifor Cortex-M chips, which has nostd, only thecoreandallocparts of the standard library.
Two platform changes to know
Intel Macs moved from Tier 1 to Tier 2 with host tools in Rust 1.90, so they are still built but no longer tested on
every change. Windows on Arm, aarch64-pc-windows-msvc, became Tier 1 in Rust 1.91.
The toolchain file says which compiler builds the project. A package can also say which is the oldest release it
supports, with rust-version in its Cargo.toml, and Cargo then refuses an older compiler with a clear message instead
of a confusing error later:
from session import run
# A package that says it needs Rust 1.99 or later, built with the pinned 1.99.0 and then with this computer's stable.
run("""
cd pinned/canteen
cat Cargo.toml
cargo run
!cargo +stable run
""") Output
$ cd pinned/canteen
$ cat Cargo.toml
[package]
name = "canteen"
version = "0.1.0"
edition = "2024"
rust-version = "1.99"
[dependencies]
$ cargo run
Compiling canteen v0.1.0 (~/pinned/canteen)
Finished `dev` profile [unoptimized + debuginfo] target(s) in …
Running `target/debug/canteen`
canteen built with a Rust new enough for it
$ cargo +stable run
error: rustc 1.98.0 is not supported by the following package:
canteen@0.1.0 requires rustc 1.99
[exit status 101]
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 msrv_session.py
With the pinned 1.99.0 the package built and ran. With +stable, this computer’s 1.98.0, Cargo stopped before
compiling anything: rustc 1.98.0 “is not supported”, because canteen requires 1.99.
Checkpoint
Check: Pinning with rust-toolchain.toml
Practise: pin a project of your own
Make a folder for a project, and write a rust-toolchain.toml in it that pins Rust 1.99.0 with clippy, rustfmt and
the wasm32-wasip1 target, like the file above. Then, in that folder, run rustup show active-toolchain and
rustc --version, and compare with the session in the previous section: the first line should name
1.99.0-<your host>, overridden by your file, and the second rustc 1.99.0. If rustup starts downloading, the release
or one of its parts was not installed yet, and the file is doing its job.
Then try the questions on channels, the order rustup follows and toolchain files. A wrong answer shows its reason straight away, and that idea comes back before the others:
Practice that adapts to you
Practise this lesson
11 questions about 4 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Interview questions
Warm-up: what is the difference between rustup, rustc and cargo?
rustc is the Rust compiler: it turns source files into a library or an executable. Cargo is Rust’s build tool and
package manager: it reads Cargo.toml, fetches dependencies, calls rustc with the right options for every crate, and
runs tests, benchmarks and documentation builds. rustup is neither; it installs and manages toolchains, each one a
release of rustc, Cargo and the standard library, and its proxies in ~/.cargo/bin decide which toolchain runs each
command, from a +toolchain argument, RUSTUP_TOOLCHAIN, a directory override, a rust-toolchain.toml file or the
default. A good answer adds the practical consequence: when a build behaves differently on two computers,
rustup show active-toolchain and rustc --version show which compiler each one really uses.
Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.
-
Mid to senior
Every laptop builds the project, but CI fails the morning after a new stable release: a new Clippy lint fires, and CI treats warnings as errors. How do you make builds reproducible without falling behind on Rust releases?
-
Mid to senior
Your library promises to build on Rust 1.85, while the team develops on the newest stable release. How do you set this up, and how do you check the promise on every change?
Key takeaways
- rustup installs toolchains, each a release of
rustc,cargoand the standard library, and its commands in~/.cargo/binstart the toolchain that applies; Rust links with the system’s linker, so install one first. - Stable ships every six weeks and is for all your work, beta is the next stable release to test against, and nightly is only for unfinished features, which stable refuses with E0554.
- rustup uses the first choice it finds: a +toolchain argument,
RUSTUP_TOOLCHAIN, a directory override, arust-toolchain.tomlfile, then the default, andrustup show active-toolchainsays which one won. - A committed
rust-toolchain.tomlpins the release, its components and its targets for every computer, whilerust-versioninCargo.tomlstates the oldest release a package supports.
Terms in this lesson
- component
- A part of a toolchain that rustup adds or removes on its own, such as clippy, rustfmt, rust-docs, rust-src or rust-analyzer, or the standard library for one target.
- release channel
- A stream of Rust releases: stable, a new release every six weeks; beta, the next stable release while it is tested; and nightly, built every night with features that are not finished yet.
- rustup
- The installer and manager of Rust toolchains. Its commands in ~/.cargo/bin, cargo and rustc among them, are proxies that choose a toolchain for each run and hand the command to that toolchain's own program.
- target
- The kind of system a program is built for, named by a triple such as aarch64-apple-darwin, x86_64-pc-windows-msvc or wasm32-wasip1. Building for a target needs that target's standard library.
- toolchain
- One installed release of Rust's tools for one host: rustc, Cargo and the standard library, plus components such as rustfmt and Clippy. rustup names toolchains such as stable-aarch64-apple-darwin or 1.99.0-aarch64-apple-darwin.
References
- Install Rust (The Rust Project)
- The Rust Programming Language: Installation (The Rust Project)
- The rustup book (The Rust Project)
- The rustup book: Concepts (The Rust Project)
- The rustup book: Channels (The Rust Project)
- The rustup book: Components (The Rust Project)
- The rustup book: Profiles (The Rust Project)
- The rustup book: Overrides (The Rust Project)
- The rustup book: Environment variables (The Rust Project)
- The Rust Programming Language: How Rust is Made and Nightly Rust (The Rust Project)
- The rustc book: Platform Support (The Rust Project)
- The rustc book: thumbv7m-none-eabi (The Rust Project)
- The Cargo Book: The rust-version field (The Rust Project)
- Announcing Rust 1.90.0 (The Rust Project)
- Announcing Rust 1.91.0 (The Rust Project)
- Miri: an interpreter for Rust that detects undefined behaviour (The Rust Project)
- Rust error codes index: E0554 (The Rust Project)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress