Getting Started

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the section bibliography.

This page is written against Rust 1.99.0 (stable since 2026-10-01) and the 2024 edition, checked on 2026-10-06. It gets a working toolchain onto your machine, compiles a first program by hand and with Cargo, and walks through a small interactive program so that the rest of the section has something concrete to refer to. The next page, Toolchain, rustup & Editions, explains the toolchain in depth.

What Rust is for

Rust is a compiled, statically typed systems programming language. Its defining property is that it gives you the control of C and C++ — values on the stack by default, no garbage collector, no runtime, predictable layout, direct access to the operating system — while ruling out whole classes of memory bugs at compile time: use-after-free, double free, dangling pointers, iterator invalidation and data races. It does this with an ownership system checked by the compiler (see Ownership & Moves) rather than with a runtime collector, so the safety has no run-time cost.

In practice Rust is used for:

Domain Typical uses

Systems software

operating-system components, drivers, file systems, databases, browsers, virtual machines

Command-line tools

fast, single-binary tools such as ripgrep, fd or the Cargo tool itself; see CLI frameworks in Go and Rust for clap and Ratatui

Network services and backends

HTTP APIs and proxies on tokio, hyper and axum; see the Axum section

WebAssembly

code that runs in the browser or in a WASI host; see WebAssembly

Embedded

no_std firmware for microcontrollers (see the community Embedded Rust Book)

Extending other languages

native modules for Python, Node.js or C programs; see FFI & C Interop and Python Interop with PyO3

How the language evolves

Rust is developed in the open by the Rust Project, organised in teams (language, compiler, library, Cargo, and so on). The Rust Foundation, an independent non-profit, holds the trademarks and infrastructure and funds work on the project; it does not design the language. Significant changes go through the RFC (Request for Comments) process: an RFC is a design document discussed publicly and accepted by the relevant team, then implemented behind a feature gate on nightly and stabilised once it is ready. Stable releases ship every six weeks, and editions (2015, 2018, 2021, 2024) bundle the few changes that are not backwards compatible. The toolchain page covers the release train and editions, and What’s New & Edition Migration lists what changed release by release.

Installing Rust with rustup

The supported way to install Rust is rustup, the toolchain installer and version manager. It installs rustc (the compiler), cargo (the build tool and package manager), the standard library, rustdoc, clippy and rustfmt, and lets you switch between versions later.

On Linux and macOS, run the official installer script and accept the default installation:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# ...
# Rust is installed now. Great!
# To get started you may need to restart your current shell.
# This would reload your PATH environment variable to include
# Cargo's bin directory ($HOME/.cargo/bin).
. "$HOME/.cargo/env"

The installer puts everything under ~/.rustup (toolchains) and ~/.cargo (Cargo’s home, including ~/.cargo/bin with the proxies for rustc, cargo and friends). Rust needs a linker: on Linux install your distribution’s C toolchain (build-essential on Debian/Ubuntu, gcc on Fedora), and on macOS install the Xcode command-line tools with xcode-select --install.

On Windows, download and run rustup-init.exe from https://rustup.rs. The default x86_64-pc-windows-msvc host needs the Microsoft C++ build tools ("Desktop development with C++" in the Visual Studio installer), which the installer offers to set up. Alternatively, winget install --id Rustlang.Rustup installs the same rustup.

Check the installation:

rustc --version
# rustc 1.99.0 (b940084d7 2026-09-28)
cargo --version
# cargo 1.99.0 (...)
rustup --version
# rustup 1.29.x (...)

Keep the toolchain current with rustup update, and remove everything with rustup self uninstall. The local copy of the official documentation, including The Book and the standard library API, opens offline with rustup doc (rustup doc --std, rustup doc --book).

Editor setup

Every major editor gets IDE features from rust-analyzer, the official language server: completion, go to definition, inline types and parameter names, inline compiler errors, refactorings, and running tests from the editor.

  • VS Code: install the rust-analyzer extension (publisher: rust-lang). Add the CodeLLDB extension for debugging (see Debugging & Diagnostics).

  • JetBrains: RustRover, or the Rust plugin for IntelliJ-based IDEs, uses its own analyser.

  • Neovim, Helix, Emacs, Zed, Sublime Text: configure their LSP client to start rust-analyzer; install the binary with rustup component add rust-analyzer.

rust-analyzer runs cargo check (or Clippy, if you configure rust-analyzer.check.command = "clippy") on save, so diagnostics in the editor are the same ones the compiler reports. More on tooling in Tooling: Clippy, rustfmt & rust-analyzer.

Hello, world! with rustc

A Rust program is a set of source files whose entry point is a function called main. Save this as main.rs:

fn main() {
    println!("Hello, world!");
}
rustc main.rs
./main          # main.exe on Windows
# Hello, world!

Three details are worth noticing even in this tiny program:

  • fn main() takes no arguments and returns nothing (the unit type ()); it can also return a Result, as shown in Error Handling.

  • println! ends in !, so it is a macro, not a function. Macros expand to code at compile time; println! checks the format string against its arguments while compiling. See Macros.

  • Statements end with ;. Rust is an expression language, and the semicolon turns an expression into a statement (see Functions & Expressions).

rustc produces a native executable with no runtime dependency beyond the C library. Calling rustc directly is fine for one file, but real projects use Cargo.

Hello, Cargo!

Cargo is Rust’s build system and package manager. It creates projects, downloads and compiles dependencies (crates) from https://crates.io, builds your code, runs tests and benchmarks, generates documentation and publishes packages.

cargo new hello_cargo
#     Creating binary (application) `hello_cargo` package
cd hello_cargo
tree -a -I .git
# .
# ├── .gitignore
# ├── Cargo.toml
# └── src
#     └── main.rs

cargo new also initialises a Git repository (pass --vcs none to skip it) and uses the newest edition. cargo new --lib name creates a library crate with src/lib.rs instead. The manifest, Cargo.toml, describes the package:

[package]
name = "hello_cargo"
version = "0.1.0"
edition = "2024"

[dependencies]

The everyday commands are:

Command What it does

cargo build

compiles the package and its dependencies into target/debug/ (unoptimised, with debug info)

cargo run

builds if needed, then runs the binary; arguments after -- go to your program

cargo check

type-checks and borrow-checks without generating code — much faster, ideal while editing

cargo build --release

optimised build into target/release/; use it for benchmarks and deployment

cargo test

builds and runs unit, integration and documentation tests

cargo doc --open

builds API documentation for your crate and its dependencies

cargo fmt / cargo clippy

formats the code / runs the Clippy linter

cargo add rand

adds a dependency to Cargo.toml (see Cargo & Workspaces)

cargo run
#    Compiling hello_cargo v0.1.0 (/home/me/hello_cargo)
#     Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.31s
#      Running `target/debug/hello_cargo`
# Hello, world!

The first build writes Cargo.lock, which records the exact version of every dependency so builds are reproducible. Commit it for applications (and, since 2023, Cargo’s guidance is to commit it for libraries too).

A first real program: the guessing game, condensed

The Book’s chapter 2 builds a number-guessing game to show many features at once. Here is a condensed version that uses the rand crate (0.10) for the secret number. Add the dependency first:

cargo new guessing_game && cd guessing_game
cargo add rand@0.10
use std::cmp::Ordering;
use std::io;

fn main() {
    println!("Guess the number between 1 and 100!");
    let secret: u32 = rand::random_range(1..=100);

    loop {
        println!("Please input your guess.");
        let mut guess = String::new();
        io::stdin().read_line(&mut guess).expect("failed to read line");

        // Shadow the String with a number; skip input that does not parse.
        let guess: u32 = match guess.trim().parse() {
            Ok(n) => n,
            Err(_) => {
                println!("That is not a number.");
                continue;
            }
        };

        match guess.cmp(&secret) {
            Ordering::Less => println!("Too small!"),
            Ordering::Greater => println!("Too big!"),
            Ordering::Equal => {
                println!("You win!");
                break;
            }
        }
    }
}

What each part introduces, with the page that covers it:

  • use std::cmp::Ordering; brings a name into scope — see Modules, Crates & Visibility.

  • let mut guess = String::new(); declares a mutable variable; variables are immutable by default — see Variables, Mutability & Constants.

  • &mut guess passes a mutable reference, so read_line can append to the string without taking ownership — see Borrowing & References.

  • read_line returns an io::Result<usize>; .expect(...) panics with a message on error, acceptable in a toy program — see Option, Result & Combinators.

  • let guess: u32 = ... shadows the previous guess with a value of a different type.

  • match compares a value against patterns and must cover every case — see Enums & Pattern Matching.

  • loop, continue and break — see Control Flow.

The Book (written against rand 0.8) calls rand::thread_rng().gen_range(1..=100). rand 0.9 renamed these to rand::rng() and random_range and added free functions such as rand::random_range (used above); rand 0.10 keeps those names but renames the extension trait that provides random_range on a generator from Rng to RngExt. Older tutorials that use thread_rng and gen_range no longer compile against current rand.

How rustc compiles a crate

It helps to know roughly what happens between cargo build and an executable, because compiler errors come from specific stages:

Diagram of the rustc pipeline. Source files are lexed into tokens, parsed into an AST, lowered to HIR, then to THIR and MIR, then to LLVM IR and finally machine code that the linker combines. Macro expansion and name resolution run on the AST, type checking and trait solving on the HIR, borrow checking on the MIR, and monomorphization happens before LLVM optimizes. cargo check stops after the MIR checks.
  • Lexing and parsing turn text into tokens and an abstract syntax tree (AST). Syntax errors come from here.

  • Macro expansion and name resolution run on the AST: println! and #[derive(...)] are expanded, use paths are resolved. "cannot find value" (E0425) errors come from here.

  • The AST is lowered to the HIR (high-level IR, with for loops and ? desugared), where type checking and trait resolution happen. Type mismatches (E0308) and missing trait implementations (E0277) come from here.

  • The HIR is lowered (through THIR) to the MIR, a control-flow graph on which the borrow checker runs. Ownership and borrowing errors (E0382, E0499, E0502) come from here.

  • Generic code is monomorphised — a copy is generated for each concrete type — and handed to LLVM, which optimises and emits machine code. Link errors come from the system linker at the very end.

cargo check runs everything up to and including the MIR checks but skips code generation, which is why it is the fastest way to find errors.

Experimenting without installing: the Playground and Rustlings

The Rust Playground (https://play.rust-lang.org) compiles and runs code in the browser on stable, beta or nightly, in debug or release mode, with the top 100 crates of crates.io available. It can also show the expanded macros, MIR, LLVM IR and assembly for a snippet, run Clippy and rustfmt, and share a snippet as a link — the usual way to ask for help on the users forum.

Rustlings (https://rustlings.rust-lang.org) is the official set of small exercises that accompany The Book. Each exercise is a file that does not compile or fails a test; you fix it, and the watcher moves on to the next one.

cargo install rustlings
rustlings init      # creates a rustlings/ directory with the exercises
cd rustlings
rustlings           # watch mode: edit the current exercise until it passes

Source: Rustlings

Reading compiler errors

Rust’s compiler is known for detailed diagnostics. Every error has the same structure: a code, a message, the location with the offending span underlined, labels that explain the reasoning, and often a help: suggestion that you can apply verbatim. Take this program, which uses a String after giving it away:

fn main() {
    let title = String::from("Dune");
    let moved = title;
    println!("{title} / {moved}"); // error[E0382]: borrow of moved value: `title`
}

Source: Error E0382

cargo check
# error[E0382]: borrow of moved value: `title`
#  --> src/main.rs:4:16
#   |
# 2 |     let title = String::from("Dune");
#   |         ----- move occurs because `title` has type `String`, which does not implement the `Copy` trait
# 3 |     let moved = title;
#   |                 ----- value moved here
# 4 |     println!("{title} / {moved}");
#   |                ^^^^^ value borrowed here after move
#   |
# help: consider cloning the value if the performance cost is acceptable
#   |
# 3 |     let moved = title.clone();
#   |                      ++++++++

Read it from the top: the primary label (^^^^^) says where the problem is detected, and the secondary labels (-----) explain why. Every error code has a long-form explanation with examples in the compiler error index, also available offline:

rustc --explain E0382
# A variable was used after its contents have been moved elsewhere.
# ...

A few habits make errors easier to deal with:

  • Fix the first error first. Later errors are often consequences of the first one.

  • Run cargo check often, or rely on rust-analyzer, so each error appears next to the edit that caused it.

  • Read the help: lines. For borrowing errors the suggestion (clone, borrow instead of move, add a lifetime) is usually one of a few standard fixes discussed on the ownership pages.

  • Warnings matter. unused_must_use (an ignored Result) and unused_variables point at real bugs surprisingly often; Clippy adds hundreds more lints.

The error codes this section refers to most are E0382 (use after move), E0499 and E0502 (conflicting borrows), E0597 (value does not live long enough), E0106 (missing lifetime specifier), E0277 (trait not implemented) and E0308 (mismatched types).

Where the books are outdated

The introductory chapters of the books consulted for this section use editions 2018 and 2021, rand 0.8’s thread_rng/gen_range, and older tool versions. Yoon’s Beginning Axum (chapter 1) presents a Rust refresher for backend developers and states that Rust catches memory leaks at compile time; that is not correct — leaks are memory-safe in Rust and are possible with Rc cycles or mem::forget (see Smart Pointers). New projects created with a current Cargo default to edition 2024, which the whole section targets.