Rust Programming: Getting Started Guide
Rust is a systems programming language focused on safety, speed, and concurrency. Developed by Mozilla and now stewarded by the Rust Foundation, it has been voted the “most loved language” on the Stack Overflow developer survey for multiple years running. Rust guarantees memory safety without a garbage collector, making it ideal for performance-critical and reliability-sensitive software.
This guide walks through getting started with Rust — installation, project setup, basic syntax, and the concepts that make Rust unique.
Why Rust?
Rust solves problems that have plagued systems programming for decades — buffer overflows, use-after-free errors, and data races — at compile time, without runtime overhead.
- Memory safety — The compiler enforces ownership and borrowing rules, eliminating entire categories of bugs
- Zero-cost abstractions — High-level constructs compile down to efficient machine code with no hidden cost
- Fearless concurrency — The type system catches data races at compile time
- Excellent tooling — Cargo (build system), rustfmt (formatter), clippy (linter), and rust-analyzer (IDE support)
- Strong ecosystem — crates.io hosts over 150,000 packages
Rust is used in the Linux kernel, Firefox, Dropbox, Figma, and AWS infrastructure. It powers CLI tools like ripgrep, fd, and bat, as well as web frameworks like Axum and Actix.
Installing Rust
The recommended installation method is rustup, the Rust toolchain installer:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shThis installs rustc (compiler), cargo (package manager and build tool), and rustup (toolchain manager).
rustc --version # rustc 1.80.0 (051478957 2024-07-21)
cargo --version # cargo 1.80.0 (376290515 2024-07-16)On Windows, download the installer from rustup.rs or use winget install Rustup.
Editor Support
Install rust-analyzer for IDE features. It is available as a VS Code extension, a Neovim plugin, and a standalone LSP server.
Hello, World!
Create a new Rust file:
fn main() {
println!("Hello, world!");
---rustc hello.rs
./hello
# Hello, world!The fn keyword declares a function. main is the entry point. println! is a macro (indicated by the !) that prints text with a newline. Rust distinguishes macros from functions with the trailing !.
Cargo: The Rust Build System
Cargo is Rust’s build system and package manager. Initialize a new project:
cargo new hello_cargo
cd hello_cargoThis creates:
hello_cargo/
├── Cargo.toml # Project manifest
└── src/
└── main.rs # Source codeCargo.toml
[package]
name = "hello_cargo"
version = "0.1.0"
edition = "2021"
[dependencies]cargo build # Compile (debug mode)
cargo run # Compile and run
cargo check # Check code without producing binary (faster)
cargo build --release # Optimized buildAlways use cargo run during development. Use cargo build --release for production binaries — it produces significantly faster code at the cost of longer compilation time.
Adding Dependencies
[dependencies]
serde = { version = "1.0", features = ["derive"] }
reqwest = { version = "0.12", features = ["json"] }
tokio = { version = "1", features = ["full"] }cargo add serde --features derive
cargo add reqwest --features json
cargo add tokio --features fullBasic Syntax
Variables and Mutability
Variables are immutable by default:
let x = 5;
// x = 6; // Compile error — cannot assign twice to immutable variable
let mut y = 5;
y = 6; // OK — y is mutable
const MAX_POINTS: u32 = 100_000; // Constants, always immutable
Shadowing
You can declare a new variable with the same name as a previous one:
let x = 5;
let x = x + 1; // Shadows previous x
let x = x * 2; // Shadows again
println!("{}", x); // 12
Shadowing is different from mutation because it creates a new binding and can change the type:
let spaces = " ";
let spaces = spaces.len(); // Changes type from &str to usize
Data Types
// Scalar types
let a: i32 = -42; // Signed integer (i8, i16, i32, i64, i128)
let b: u32 = 42; // Unsigned integer
let c: f64 = 3.14; // Float (f32, f64)
let d: bool = true; // Boolean
let e: char = '🦀'; // Character (4 bytes, Unicode)
// Compound types
let tup: (i32, f64, u8) = (500, 6.4, 1);
let (x, y, z) = tup; // Destructuring
println!("{}", tup.0); // Index access
let arr: [i32; 3] = [1, 2, 3];
println!("{}", arr[0]); // Array access
Functions
fn add(x: i32, y: i32) -> i32 {
x + y // No semicolon = return value (expression)
---
// Equivalent with explicit return
fn add_explicit(x: i32, y: i32) -> i32 {
return x + y;
---In Rust, the last expression in a block is automatically returned. Statements end with semicolons; expressions do not.
Control Flow
// if expression (returns a value)
let condition = true;
let number = if condition { 5 } else { 6 };
// loop with break value
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2;
}
---;
println!("{}", result); // 20
// for loop
let arr = [10, 20, 30];
for element in arr {
println!("{}", element);
---
// Range
for number in 1..=3 {
println!("{}", number);
---Ownership: The Core Concept
Rust’s ownership system is what makes it safe without garbage collection:
- Each value has an owner
- There can only be one owner at a time
- When the owner goes out of scope, the value is dropped
let s1 = String::from("hello");
let s2 = s1; // s1 is moved to s2
// println!("{}", s1); // Compile error — value moved
let s3 = s2.clone(); // Deep copy (expensive)
println!("{}", s2); // OK — s2 still owns the value
Pattern Matching
Match is Rust’s powerful control flow construct:
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
---
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
---Error Handling
Rust does not have exceptions. It uses Result<T, E> and Option<T>:
use std::fs::File;
fn read_file() -> Result<String, std::io::Error> {
let mut file = File::open("hello.txt")?; // ? propagates error
let mut contents = String::new();
file.read_to_string(&mut contents)?;
Ok(contents)
---Next Steps
Rust has a learning curve — the borrow checker can be strict at first, but the compiler errors are famously helpful and often include suggested fixes. Stick with it; the safety guarantees and performance are worth the initial friction.
Try these next:
- Work through The Rust Book
- Build a CLI tool with
clap - Explore web development with
axum - Contribute to an open-source Rust project
The Rust Toolchain
Installing Rust via rustup gives you access to rustc (the compiler), cargo (the build system), rustfmt (the formatter), and clippy (the linter). Use rustup update to stay current with the latest stable release. The Rust compiler produces detailed, actionable error messages that guide you toward correct code. Many newcomers describe Rust’s compiler as their best teacher — it explains not just what is wrong but why and how to fix it.
Common First Projects
New Rust programmers build confidence with: command-line tools (grep clone, JSON formatter), web servers (Actix-Web or Axum), file format parsers (CSV, TOML), and network tools (port scanner, HTTP client). Each project introduces Rust-specific concepts like ownership, error handling, and the type system while producing immediately useful programs.
Rust in Production: Case Studies
Rust has been adopted in production by major technology companies for performance-critical and safety-critical systems. Mozilla uses Rust in the Firefox browser engine (Servo/Quantum) for improved memory safety and performance in CSS layout and rendering. Dropbox rewrote their sync engine core in Rust, achieving significant performance improvements and eliminating entire classes of bugs. Cloudflare uses Rust for edge computing infrastructure, including their Pingora HTTP proxy that handles 10% of the world’s internet traffic. Figma rewrote their multiplayer editing engine in Rust, achieving predictable performance and eliminating crashes. Discord used Rust for their read states service, handling millions of concurrent connections with minimal resources. These case studies demonstrate Rust’s value in systems where performance, reliability, and memory safety are critical. The common pattern: rewrite performance-critical components in Rust while maintaining the rest of the system in the original language, proving that incremental adoption works in practice.
Embedded Systems Programming
Rust is gaining traction in embedded systems for its memory safety guarantees without a garbage collector or runtime. The cortex-m-quickstart template provides a starting point for ARM Cortex-M microcontrollers. The RTIC framework provides real-time interrupt-driven concurrency. Embassy offers an async embedded runtime with USB, networking, and BLE support. Rust’s type system prevents common embedded errors like misconfiguring peripherals, incorrect register access, and data races between interrupt handlers and main code. The embedded ecosystem continues to mature with HAL implementations for major microcontroller families from STM, Nordic, and Espressif.
FAQ
What background knowledge is recommended? A basic understanding of programming fundamentals will help, but many concepts are explained from first principles.
How can I practice these skills? Start with small projects, contribute to open source, and build a portfolio. Consistent practice is more effective than occasional deep dives.
What tools do I need to get started? Most topics require only a text editor, the relevant runtime, and package manager. Specific tool recommendations are included throughout.
For a comprehensive overview, read our article on Rust Async Programming.
For a comprehensive overview, read our article on Rust Cargo Modules.