Getting Started

What Rust is, how rustup and Cargo fit together, and how to read a small program before you write one.

Getting started

Start here, and stay in this order. This lesson only names the language, the tools, and the shapes of a small program. Ownership, errors, and traits come in later lessons, after you can read a file.

What Rust is

Rust is a systems programming language. You use it to write programs that sit close to memory, the CPU, and threads, without a garbage collector at runtime, and without giving up the safety that C and C++ often leave to you.

You write source files. The compiler, rustc, together with Cargo, checks ownership, types, and lifetimes before the program runs. If those checks pass, you get machine code: a native binary. There is no virtual machine the way Java or Python needs one.

Source.rs filesrustc + Cargochecks firstNative binaryno VM
StatusYou write source

Nothing is running yet. The file is a description the compiler has not accepted.

Why production teams pick it

ReasonWhat you get
Memory safetyNo garbage-collector pause. Use-after-free, double-free, and data races are caught at compile time.
Predictable performanceA high-level abstraction is supposed to compile down to tight machine code. That idea is a zero-cost abstraction.
ConcurrencyThe type system can reject a program when two threads would race.
ToolingCargo builds, tests, and publishes. rustfmt formats. clippy lints. rust-analyzer is the editor engine.

Where it shows up

PlaceExamples
Command-line toolsripgrep, fd, bat
Web backends and APIsaxum, actix, tonic for gRPC
Embedded and IoTDevices with tight memory and no pause budget
Cloud infrastructureProxies, databases, observability agents
WebAssemblyIn the browser and at the edge

Hold one split from here on. Compile time is where Rust is strict. Runtime is where the program is fast. Other languages often say run it and fix crashes later. Rust will not compile until the dangerous cases in safe code are handled.

C / C++controlRustcontrol, checkedGosimpler, GCPython / JSfewer footgunssame control
StatusControl versus safety

Languages trade how close you sit to memory for how many ways the program can hurt itself.

The rest of this course is numbered. Learn it in that order. Ownership, error handling, and traits are the parts people skip and then cannot read real code. They wait until you have bindings, types, and control flow.

01_what_is_rust.rsRust
fn main() {
    println!("Rust: systems language + safety + speed.");
}

Install and tooling

Everything after this file depends on a real toolchain. Install with rustup, not with a one-off copy of the compiler. On macOS or Linux that is the script at https://rustup.rs. Then restart the terminal, or source the Cargo env file. On Windows, download rustup-init from the same site.

Check three commands. rustc --version is the compiler. cargo --version is the build tool. rustup --version is the toolchain manager.

rustupmanagesstable / beta / nightlycomponentsCargobuildscrates, tests, docsyour projectrustccompilesnative binaryemitted
Statusrustup owns the toolchain

stable is what you ship. beta previews the next stable. nightly is experimental and only when you must. rustup default stable, then rustup update.

ToolJob
clippyThe linter. Treat its warnings seriously.
rustfmtThe formatter. One style for the team.
rust-analyzerThe language server. Install the Rust extension in your editor so it can talk to it.

First-day order: install rustup, confirm rustc and Cargo, add clippy and rustfmt, connect the editor, then compile one file. From this folder, rustc 02_install_and_tooling.rs and run the binary. If it prints, the compiler works.

02_install_and_tooling.rsRust
fn main() {
    println!("If you see this, rustc works.");
    println!("Install: https://rustup.rs");
    println!("Then: rustup component add clippy rustfmt");
}

Hello world and Cargo

A Rust program is a function named main. Execution of a binary starts there. println! prints a line to standard output. The exclamation mark means it is a macro. Macros expand at compile time. You call them like functions, but they are not functions.

You can compile one lesson file with rustc. That is useful while you are looking at a single file. Real projects always go through Cargo: cargo new creates the project, cargo run builds it and starts it. Once Cargo is set up, prefer cargo run over calling rustc yourself.

Layout to memorize

PathRole
Cargo.tomlPackage metadata and dependencies, the role package.json plays in other stacks.
Cargo.lockThe exact versions. Commit it for binaries.
src/main.rsThe binary entry, fn main.
src/lib.rsA library crate. It does not need main.
cargo newcreatecargo checkfast feedbackcargo testrun testscargo buildfast compilebuild --releaseship this
StatusCreate, then check

cargo check typechecks without a full link. cargo fmt formats. cargo clippy lints. cargo doc --open builds the docs.

Printing, before any other type

FormMeaning
println!("plain")Writes a line to standard output
{}Display, the user-facing form
{:?}Debug, for you
eprintln!Standard error. CLIs use that for diagnostics.

A string literal such as "hello" is a string slice, written &str, stored in the binary. The difference between that and an owned String waits until ownership and collections. You only need the literal for now.

src/main.rsRust
fn main() {
    println!("Hello, Rust.");
    println!("{} + {} = {}", 2, 3, 2 + 3);
    eprintln!("This line is stderr (still visible in terminal).");
}
Cargo.tomlText
[package]
name = "hello"
version = "0.1.0"
edition = "2021"

[dependencies]

How to read Rust

You do not need every keyword yet. You need to recognize shapes, in this order.

Bindings

let x = 10 is immutable. let mut y = 10 can change. const MAX: u32 = 100 is a compile-time constant, and the type is required. The word mut is the mark that you meant to change it. The next lesson spends a whole section on that.

Functions

fn add(a: i32, b: i32) -> i32 takes two 32-bit integers and returns one. The last expression without a semicolon is the return value. return a + b is an early return. It works. The expression form is the one you will see at the end of a function.

Blocks, types, macros, attributes

ShapeHow to read it
BlockBraces are a scope. A name declared inside dies at the closing brace.
TypeSits after a colon: let n: i32 = 5.
MacroA name ending in !. println!, format!, vec!, assert_eq!, todo!.
AttributeA line starting with a hash, an instruction to the compiler. derive(Debug) generates a debug print. test marks a test. cfg(test) compiles only when testing. The hash sits immediately before the attribute name.
if y >= 0expressionythen-yelseabsthe value
StatusA statement does work

let x = 1; is a statement. A trailing semicolon discards a value. x; does not give you x.

Names

WhatCase
Functions, variables, modulessnake_case
Types and traitsCamelCase
Constants and staticsSCREAMING_SNAKE

Keywords you will meet, not all today: let, mut, fn, if, else, match, loop, while, for, struct, enum, impl, trait, use, mod, pub, crate, async, await, unsafe, return, break, continue, where, ref, move, self, Self, Result, Option, Some, None, Ok, Err. The next lesson uses let, mut, if, loop, and match. Leave the rest until the lesson that defines them.

How to study a section. Read the rules, then the code under them. Change one value, compile again, and see what the compiler says. Do not skip ownership, error handling, or traits. Those three are what interviews and production code both assume you can read.

04_how_to_read_rust.rsRust
fn add(a: i32, b: i32) -> i32 {
    a + b
}

fn main() {
    let x = 10;
    let mut y = 5;
    y += 1;
    let abs = if y >= 0 { y } else { -y };
    println!("x={x}, y={y}, abs={abs}, sum={}", add(x, y));
}