Keyboard shortcuts

Press ← or β†’ to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Installation and Setup

What you’ll learn: How to install Rust and its toolchain, the Cargo build system vs pip/Poetry, IDE setup, your first Hello, world! program, and essential Rust keywords mapped to Python equivalents.

Difficulty: 🟒 Beginner

Installing Rust

# Install Rust via rustup (Linux/macOS/WSL)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# Verify installation
rustc --version     # Rust compiler
cargo --version     # Build tool + package manager (like pip + setuptools combined)

# Update Rust
rustup update

Rust Tools vs Python Tools

PurposePythonRust
Language runtimepython (interpreter)rustc (compiler, rarely called directly)
Package managerpip / poetry / uvcargo (built-in)
Project configpyproject.tomlCargo.toml
Lock filepoetry.lock / requirements.txtCargo.lock
Virtual envvenv / condaNot needed (deps are per-project)
Formatterblack / ruff formatrustfmt (built-in: cargo fmt)
Linterruff / flake8 / pylintclippy (built-in: cargo clippy)
Type checkermypy / pyrightBuilt into compiler (always on)
Test runnerpytestcargo test (built-in)
Docssphinx / mkdocscargo doc (built-in)
REPLpython / ipythonNone (use cargo test or Rust Playground)

IDE Setup

VS Code (recommended):

Extensions to install:
- rust-analyzer        ← Essential: IDE features, type hints, completions
- Even Better TOML     ← Syntax highlighting for Cargo.toml
- CodeLLDB             ← Debugger support

# Python equivalent mapping:
# rust-analyzer β‰ˆ Pylance (but with 100% type coverage, always)
# cargo clippy  β‰ˆ ruff (but checks correctness, not just style)

Your First Rust Program

Python Hello World

# hello.py β€” just run it
print("Hello, World!")

# Run:
# python hello.py

Rust Hello World

// src/main.rs β€” must be compiled first
fn main() {
    println!("Hello, World!");   // println! is a macro (note the !)
}

// Build and run:
// cargo run

Key Differences for Python Developers

Python:                              Rust:
─────────                            ─────
- No main() needed                   - fn main() is the entry point
- Indentation = blocks               - Curly braces {} = blocks
- print() is a function              - println!() is a macro (the ! matters)
- No semicolons                      - Semicolons end statements
- No type declarations               - Types inferred but always known
- Interpreted (run directly)         - Compiled (cargo build, then run)
- Errors at runtime                  - Most errors at compile time

Creating Your First Project

# Python                              # Rust
mkdir myproject                        cargo new myproject
cd myproject                           cd myproject
python -m venv .venv                   # No virtual env needed
source .venv/bin/activate              # No activation needed
# Create files manually               # src/main.rs already created

# Python project structure:            Rust project structure:
# myproject/                           myproject/
# β”œβ”€β”€ pyproject.toml                   β”œβ”€β”€ Cargo.toml        (like pyproject.toml)
# β”œβ”€β”€ src/                             β”œβ”€β”€ src/
# β”‚   └── myproject/                   β”‚   └── main.rs       (entry point)
# β”‚       β”œβ”€β”€ __init__.py              └── (no __init__.py needed)
# β”‚       └── main.py
# └── tests/
#     └── test_main.py
graph TD
    subgraph Python ["Python Project"]
        PP["pyproject.toml"] --- PS["src/"]
        PS --- PM["myproject/"]
        PM --- PI["__init__.py"]
        PM --- PMN["main.py"]
        PP --- PT["tests/"]
    end
    subgraph Rust ["Rust Project"]
        RC["Cargo.toml"] --- RS["src/"]
        RS --- RM["main.rs"]
        RC --- RTG["target/ (auto-generated)"]
    end
    style Python fill:#ffeeba
    style Rust fill:#d4edda

Key difference: Rust projects are simpler β€” no __init__.py, no virtual environments, no setup.py vs setup.cfg vs pyproject.toml confusion. Just Cargo.toml + src/.


Cargo vs pip/Poetry

Project Configuration

# Python β€” pyproject.toml
[project]
name = "myproject"
version = "0.1.0"
requires-python = ">=3.10"
dependencies = [
    "requests>=2.28",
    "pydantic>=2.0",
]

[project.optional-dependencies]
dev = ["pytest", "ruff", "mypy"]
# Rust β€” Cargo.toml
[package]
name = "myproject"
version = "0.1.0"
edition = "2021"          # Rust edition (like Python version)

[dependencies]
reqwest = "0.12"          # HTTP client (like requests)
serde = { version = "1.0", features = ["derive"] }  # Serialization (like pydantic)

[dev-dependencies]
# Test dependencies β€” only compiled for `cargo test`
# (No separate test config needed β€” `cargo test` is built in)

Common Cargo Commands

# Python equivalent                # Rust
pip install requests               cargo add reqwest
pip install -r requirements.txt    cargo build           # auto-installs deps
pip install -e .                   cargo build            # always "editable"
python -m pytest                   cargo test
python -m mypy .                   # Built into compiler β€” always runs
ruff check .                       cargo clippy
ruff format .                      cargo fmt
python main.py                     cargo run
python -c "..."                    # No equivalent β€” use cargo run or tests

# Rust-specific:
cargo new myproject                # Create new project
cargo build --release              # Optimized build (10-100x faster than debug)
cargo doc --open                   # Generate and browse API docs
cargo update                       # Update deps (like pip install --upgrade)

Essential Rust Keywords for Python Developers

Variable and Mutability Keywords

#![allow(unused)]
fn main() {
// let β€” declare a variable (like Python assignment, but immutable by default)
let name = "Alice";          // Python: name = "Alice" (but mutable)
// name = "Bob";             // ❌ Compile error! Immutable by default

// mut β€” opt into mutability
let mut count = 0;           // Python: count = 0 (always mutable in Python)
count += 1;                  // βœ… Allowed because of `mut`

// const β€” compile-time constant (like Python's convention of UPPER_CASE, but enforced)
const MAX_SIZE: usize = 1024;   // Python: MAX_SIZE = 1024 (convention only)

// static β€” global variable (use sparingly; Python has module-level globals)
static VERSION: &str = "1.0";
}

Ownership and Borrowing Keywords

#![allow(unused)]
fn main() {
// These have NO Python equivalents β€” they're Rust-specific concepts

// & β€” borrow (read-only reference)
fn print_name(name: &str) { }    // Python: def print_name(name: str) β€” but Python passes ref always

// &mut β€” mutable borrow
fn append(list: &mut Vec<i32>) { }  // Python: def append(lst: list) β€” always mutable in Python

// move β€” transfer ownership (happens implicitly in Rust, never in Python)
let s1 = String::from("hello");
let s2 = s1;    // s1 is MOVED to s2 β€” s1 is no longer valid
// println!("{}", s1);  // ❌ Compile error: value moved
}

Type Definition Keywords

#![allow(unused)]
fn main() {
// struct β€” like a Python dataclass or NamedTuple
struct Point {               // @dataclass
    x: f64,                  // class Point:
    y: f64,                  //     x: float
}                            //     y: float

// enum β€” like Python's enum but MUCH more powerful (carries data)
enum Shape {                 // No direct Python equivalent
    Circle(f64),             // Each variant can hold different data
    Rectangle(f64, f64),
}

// impl β€” attach methods to a type (like defining methods in a class)
impl Point {                 // class Point:
    fn distance(&self) -> f64 {  //     def distance(self) -> float:
        (self.x.powi(2) + self.y.powi(2)).sqrt()
    }
}

// trait β€” like Python's ABC or Protocol (PEP 544)
trait Drawable {             // class Drawable(Protocol):
    fn draw(&self);          //     def draw(self) -> None: ...
}

// type β€” type alias (like Python's TypeAlias)
type UserId = i64;           // UserId = int  (or TypeAlias)
}

Control Flow Keywords

#![allow(unused)]
fn main() {
// match β€” exhaustive pattern matching (like Python 3.10+ match, but enforced)
match value {
    1 => println!("one"),
    2 | 3 => println!("two or three"),
    _ => println!("other"),          // _ = wildcard (like Python's case _:)
}

// if let β€” destructure + conditional (Pythonic: if (m := regex.match(s)):)
if let Some(x) = optional_value {
    println!("{}", x);
}

// loop β€” infinite loop (like while True:)
loop {
    break;  // Must break to exit
}

// for β€” iteration (like Python's for, but needs .iter() more often)
for item in collection.iter() {      // for item in collection:
    println!("{}", item);
}

// while let β€” loop with destructuring
while let Some(item) = stack.pop() {
    process(item);
}
}

Visibility Keywords

#![allow(unused)]
fn main() {
// pub β€” public (Python has no real private; uses _ convention)
pub fn greet() { }           // def greet():  β€” everything is "public" in Python

// pub(crate) β€” visible within the crate only
pub(crate) fn internal() { } // def _internal():  β€” single underscore convention

// (no keyword) β€” private to the module
fn private_helper() { }      // def __private():  β€” double underscore name mangling

// In Python, "private" is a gentleman's agreement.
// In Rust, private is enforced by the compiler.
}

Exercises

πŸ‹οΈ Exercise: First Rust Program (click to expand)

Challenge: Create a new Rust project and write a program that:

  1. Declares a variable name with your name (type &str)
  2. Declares a mutable variable count starting at 0
  3. Uses a for loop from 1..=5 to increment count and print "Hello, {name}! (count: {count})"
  4. After the loop, print whether count is even or odd using a match expression
πŸ”‘ Solution
cargo new hello_rust && cd hello_rust
// src/main.rs
fn main() {
    let name = "Pythonista";
    let mut count = 0u32;

    for _ in 1..=5 {
        count += 1;
        println!("Hello, {name}! (count: {count})");
    }

    let parity = match count % 2 {
        0 => "even",
        _ => "odd",
    };
    println!("Final count {count} is {parity}");
}

Key takeaways:

  • let is immutable by default (you need mut to change count)
  • 1..=5 is inclusive range (Python’s range(1, 6))
  • match is an expression that returns a value
  • No self, no if __name__ == "__main__" β€” just fn main()