Getting Started

Course 1 · Ch 1
Getting Started: cargo, rustc, and Your First Program
A compiled systems language built around one goal: memory safety without a garbage collector

Rust is a compiled, systems-level language, in the same broad family as Go — source code is built into a standalone executable, not interpreted line by line. But the two languages exist for genuinely different reasons, and that difference shapes everything in this course.

Why Rust Exists

Go, JavaScript, Python, Ruby, PHP, and Kotlin all rely on a garbage collector — a background process that automatically frees memory the program no longer needs. This is convenient, but it comes with real costs: runtime overhead, and pause times that are hard to predict exactly when they'll happen. Older systems languages like C and C++ skip the garbage collector entirely, giving the programmer full manual control — but with it, real risk: dangling pointers, buffer overflows, use-after-free bugs that have caused decades of security vulnerabilities.

Rust's entire reason for existing is refusing to accept that trade-off: manual-control-level performance and predictability, without the memory-safety bugs manual control usually invites — enforced not at runtime, but by the compiler itself, before the program ever runs. Chapters 3–4 cover the actual mechanism (ownership and borrowing); this chapter is just naming the goal everything else in this course serves.

Installing Rust: rustup and rustc

rustup is Rust's official installer and toolchain manager — it installs and manages Rust versions, similar in spirit to a version manager, but built and maintained by the Rust team itself as the standard, recommended way to get Rust at all.

# after installing via rustup: rustc --version # rustc 1.76.0 (07dca489a 2024-02-04)

rustc is the raw compiler underneath everything — the direct equivalent of what runs behind the scenes when Go's go build compiles a file. In practice, most day-to-day work goes through cargo instead of calling rustc directly.

cargo: Rust's Build Tool & Package Manager

This is the first real toolchain difference from Go. Go's toolchain is deliberately minimal — go run and go build are close to the whole story. cargo bundles far more into one official tool from day one: building, running, dependency management against crates.io (Rust's package registry), and testing — closer in spirit to npm and a bundler combined, except built directly into Rust's own official tooling rather than assembled from separate third-party choices.

cargo new hello_rust # creates: # hello_rust/ # Cargo.toml (the project manifest — name, version, dependencies) # src/main.rs (your actual code) cd hello_rust cargo run

Cargo.toml is the direct equivalent of Go's go.mod — but where a go.mod stays minimal, Cargo.toml is the central place dependencies, metadata, and build configuration all live together.

The Smallest Rust Program

fn main() { println!("Hello, Rust!"); }

fn main() is the entry point — the same role func main() plays in Go; execution always starts here. println! is Rust's equivalent of fmt.Println, but notice the ! — that marks it as a macro, not an ordinary function call. Macros are a genuinely different Rust concept from anything in Go or JavaScript (Course 3's own Macros chapter covers writing them); for now, just recognize that ! after a name means "this is a macro," and println! is the one used constantly throughout this course.

ConceptGoRust
Compile & run in one stepgo run file.gocargo run
Produce a standalone binarygo buildcargo build --release
Project manifestgo.mod (minimal)Cargo.toml (build config + dependencies)
Print a linefmt.Println(...)println!(...)
Entry pointfunc main()fn main()
Why memory safety keeps coming up
Every chapter in this course's first half builds toward the same destination: how Rust guarantees memory safety at compile time, with zero runtime garbage collector. Chapter 3's ownership model is the actual mechanism — this chapter is just establishing why that mechanism is worth learning in the first place.
Semicolons are meaningful, not optional style
Unlike idiomatic Go (which omits semicolons via automatic insertion) and unlike JavaScript (where they're often optional), Rust genuinely distinguishes a statement (ending in ;) from an expression (no semicolon, and its value can be returned). Inside main right now this mostly just means "don't forget the semicolon" — but this exact distinction becomes meaningful later when a function's last line, with no semicolon, becomes its return value. Missing or extra semicolons produce real compiler errors, not silent behavior differences.

Coding Challenges

Challenge 1

Use cargo new to create a project called greeting, then edit src/main.rs so it prints your name and a short greeting using two separate println! calls. Run it with cargo run.

📄 View solution
Challenge 2

Write a program that prints "Year: 2026" and "Language: Rust" using println! with placeholders ({}) instead of concatenating strings — two separate println! calls, each using a placeholder.

📄 View solution
Challenge 3

Explain, in your own words, why Rust's pitch ("manual-control performance without manual-control memory bugs") is a genuinely different trade-off than either a garbage-collected language or C/C++ — and name the two chapters coming up that actually deliver on that promise.

📄 View solution

Chapter 1 Quick Reference

  • Rust's core goal: memory safety without a garbage collector, enforced at compile time
  • rustup — the official installer/toolchain manager; rustc — the raw compiler underneath
  • cargo — Rust's official build tool + package manager + test runner, bundled together (unlike Go's minimal toolchain)
  • Cargo.toml — the project manifest (dependencies, build config) — Rust's fuller-featured counterpart to Go's go.mod
  • cargo new / cargo run / cargo build --release — create, run, and produce a standalone binary
  • fn main() { } — the entry point, same role as Go's func main()
  • println!(...) — note the !: this is a macro, not an ordinary function
  • Semicolons distinguish statements from expressions — a distinction that becomes load-bearing later, not just style
  • Next chapter: variables, basic types, immutability by default (let vs let mut), and shadowing