Rust: Safe Systems Programming
Learn Rust, the language that guarantees memory safety without a garbage collector
Rust: Safe Systems Programming
Rust is a systems programming language that combines low-level performance with compile-time safety guarantees. Created by Mozilla and released in 2010, Rust eliminates entire classes of memory-related bugs through its unique ownership system, without sacrificing performance.
Why Rust? Rust has been voted the “most loved language” in the Stack Overflow Survey for 8 consecutive years. Companies like Microsoft, Google, Amazon, and Discord use Rust in production.
Fundamental Concepts
Ownership, Borrowing, and Lifetimes
The ownership system is the heart of Rust:
// Ownership - each value has a single owner
fn main() {
let s1 = String::from("hello");
let s2 = s1; // s1 is moved to s2
// println!("{}", s1); // ERROR! s1 is no longer valid
println!("{}", s2); // OK
}
// Borrowing - immutable references
fn calculate_length(s: &String) -> usize {
s.len()
}
// Mutable borrowing
fn add_text(s: &mut String) {
s.push_str(" world");
}
fn main() {
let mut text = String::from("hello");
// Multiple immutable references are allowed
let r1 = &text;
let r2 = &text;
println!("{} and {}", r1, r2);
// Only ONE mutable reference at a time
let r3 = &mut text;
r3.push_str("!");
println!("{}", r3);
}
// Lifetimes - ensure references are valid
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
Data Types
// Primitive types
let integer: i32 = 42;
let floating: f64 = 3.14;
let boolean: bool = true;
let character: char = '🦀';
// Tuples
let tuple: (i32, f64, &str) = (42, 3.14, "rust");
let (x, y, z) = tuple; // Destructuring
// Arrays (fixed size)
let array: [i32; 5] = [1, 2, 3, 4, 5];
let first = array[0];
// Vectors (dynamic size)
let mut vec: Vec<i32> = vec![1, 2, 3];
vec.push(4);
// Strings
let s1: &str = "string literal"; // String slice
let s2: String = String::from("owned string");
// HashMap
use std::collections::HashMap;
let mut map: HashMap<&str, i32> = HashMap::new();
map.insert("key", 42);
Structs and Enums
// Struct
#[derive(Debug, Clone)]
struct User {
name: String,
email: String,
age: u32,
active: bool,
}
impl User {
// Constructor
fn new(name: String, email: String, age: u32) -> Self {
Self {
name,
email,
age,
active: true,
}
}
// Method
fn greet(&self) -> String {
format!("Hello, {}!", self.name)
}
// Mutable method
fn deactivate(&mut self) {
self.active = false;
}
}
// Enum with data
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(u8, u8, u8),
}
// Pattern matching
fn process_message(msg: Message) {
match msg {
Message::Quit => println!("Quitting..."),
Message::Move { x, y } => println!("Moving to ({}, {})", x, y),
Message::Write(text) => println!("Text: {}", text),
Message::ChangeColor(r, g, b) => println!("Color: RGB({}, {}, {})", r, g, b),
}
}
// Option and Result
fn divide(a: f64, b: f64) -> Option<f64> {
if b == 0.0 { None } else { Some(a / b) }
}
fn read_file(path: &str) -> Result<String, std::io::Error> {
std::fs::read_to_string(path)
}
Traits
// Defining a trait
trait Summarizable {
fn summarize(&self) -> String;
// Default implementation
fn description(&self) -> String {
format!("Summary: {}", self.summarize())
}
}
// Implementing trait
struct Article {
title: String,
author: String,
content: String,
}
impl Summarizable for Article {
fn summarize(&self) -> String {
format!("{} by {}", self.title, self.author)
}
}
// Trait bounds in generics
fn print_summary<T: Summarizable>(item: &T) {
println!("{}", item.summarize());
}
// Multiple trait bounds
fn process<T: Summarizable + Clone + std::fmt::Debug>(item: T) {
println!("{:?}", item);
}
// Where clause for readability
fn complex_function<T, U>(t: T, u: U) -> String
where
T: Summarizable + Clone,
U: std::fmt::Display,
{
format!("{}: {}", t.summarize(), u)
}
Error Handling
use std::fs::File;
use std::io::{self, Read};
// Error propagation with ?
fn read_user() -> Result<String, io::Error> {
let mut file = File::open("user.txt")?;
let mut content = String::new();
file.read_to_string(&mut content)?;
Ok(content)
}
// Custom error
#[derive(Debug)]
enum MyError {
Io(io::Error),
Parse(std::num::ParseIntError),
Custom(String),
}
impl From<io::Error> for MyError {
fn from(err: io::Error) -> Self {
MyError::Io(err)
}
}
Concurrency
use std::thread;
use std::sync::{Arc, Mutex};
// Basic threads
fn main() {
let handle = thread::spawn(|| {
for i in 1..10 {
println!("Thread: {}", i);
}
});
for i in 1..5 {
println!("Main: {}", i);
}
handle.join().unwrap();
}
// Sharing data between threads
fn shared_counter() {
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter = Arc::clone(&counter);
let handle = thread::spawn(move || {
let mut num = counter.lock().unwrap();
*num += 1;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("Result: {}", *counter.lock().unwrap());
}
Ecosystem Tools
| Tool | Purpose |
|---|---|
| cargo | Package manager and build tool |
| rustfmt | Code formatter |
| clippy | Linter with advanced tips |
| rust-analyzer | LSP for IDEs |
Learning Checklist
- Understand ownership, borrowing, and lifetimes
- Master structs, enums, and pattern matching
- Learn traits and generics
- Study error handling with Result and Option
- Explore concurrency and async/await
- Build a CLI project
- Contribute to Rust open source projects
Essential Resources
Quote
“Rust is a language that respects the developer’s time and intelligence.” - Steve Klabnik
Rust may seem challenging at first, but the safety guarantees it offers are worth the investment!