Core Language & Ownership
Core Language & Ownership Basics // Variables (immutable by default!) let x = 5; let mut y = 10; // mutable y += 1; // Type annotations let n: i32 = 42; let f: …
Core Language & Ownership
Basics
// Variables (immutable by default!)
let x = 5;
let mut y = 10; // mutable
y += 1;
// Type annotations
let n: i32 = 42;
let f: f64 = 3.14;
let b: bool = true;
let s: &str = "hello"; // string slice (borrowed)
let owned: String = String::from("hello"); // owned String
// Shadowing (re-declare, can change type)
let x = 5;
let x = x * 2; // x is now 10
let x = "now a string";
// Constants (always typed, evaluated at compile time)
const MAX_SIZE: usize = 1024;
// Primitive types
// Integers: i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize
// Floats: f32 f64
// bool, char (Unicode, 4 bytes)
// Tuples
let tup: (i32, f64, bool) = (500, 6.4, true);
let (a, b, c) = tup; // destructure
let first = tup.0;
// Arrays (fixed size, same type)
let arr: [i32; 5] = [1, 2, 3, 4, 5];
let zeros = [0; 10]; // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
// Vectors (dynamic)
let mut v: Vec<i32> = Vec::new();
v.push(1);
v.push(2);
let v2 = vec![1, 2, 3];Ownership & Borrowing
Rust's ownership system eliminates data races and memory leaks at compile time — no garbage collector, no manual free().
// Rules:
// 1. Each value has exactly one owner
// 2. When the owner goes out of scope, the value is dropped (free'd)
// 3. There can only be one mutable reference OR many immutable references (not both)
// Move semantics
let s1 = String::from("hello");
let s2 = s1; // s1 is MOVED to s2 — s1 is no longer valid
// println!("{}", s1); // COMPILE ERROR: s1 moved
// Clone (deep copy)
let s1 = String::from("hello");
let s2 = s1.clone(); // s1 still valid
// Copy trait — primitives are copied, not moved
let x = 5;
let y = x; // x is still valid (i32 implements Copy)
// References (borrowing) — doesn't take ownership
fn calculate_length(s: &String) -> usize { // borrow s
s.len()
}
let s = String::from("hello");
let len = calculate_length(&s); // pass reference
// s is still valid here
// Mutable references
fn change(s: &mut String) {
s.push_str(" world");
}
let mut s = String::from("hello");
change(&mut s);
// RULE: only one mutable reference at a time (prevents data races)
// let r1 = &mut s;
// let r2 = &mut s; // COMPILE ERROR
// println!("{} {}", r1, r2);
// Slices — reference to part of a collection
let s = String::from("hello world");
let hello = &s[0..5]; // "hello"
let world = &s[6..11]; // "world"
let arr = [1, 2, 3, 4, 5];
let slice = &arr[1..3]; // &[2, 3]Structs, Enums & Pattern Matching
// Struct
#[derive(Debug, Clone)]
struct User {
username: String,
email: String,
age: u32,
active: bool,
}
impl User {
// Constructor convention
fn new(username: String, email: String) -> User {
User { username, email, age: 0, active: true }
}
// Method — borrows self
fn greet(&self) -> String {
format!("Hello, {}!", self.username)
}
// Mutable method
fn deactivate(&mut self) {
self.active = false;
}
}
// Enum
#[derive(Debug)]
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(u8, u8, u8),
}
// Pattern matching — exhaustive
fn process(msg: Message) {
match msg {
Message::Quit => println!("Quit"),
Message::Move { x, y } => println!("Move to {x},{y}"),
Message::Write(text) => println!("Write: {text}"),
Message::ChangeColor(r, g, b) => println!("Color: {r},{g},{b}"),
}
}
// if let — single-variant match
if let Message::Write(text) = msg {
println!("Got text: {text}");
}