// Variables (immutable by default)
let name = "Alice"; // &str (string slice)
let age: i32 = 30; // i32
let height = 5.9_f64; // f64
let is_active = true; // bool
let letter = 'A'; // char
// Mutable variable
let mut count = 0;
count += 1;
// Constants (must have type annotation)
const MAX_POINTS: u32 = 100_000;
// Shadowing
let x = 5;
let x = x + 1; // New variable, shadows previous
// Integer types: i8, i16, i32, i64, i128, isize
// Unsigned: u8, u16, u32, u64, u128, usize
// Float: f32, f64
// String types
let s1: &str = "hello"; // String slice (immutable)
let s2: String = String::from("hello"); // Owned String
// String methods
let s = String::from("hello world");
s.len() // Length in bytes
s.is_empty() // Check if empty
s.contains("world") // Check substring
s.replace("world", "rust") // Replace
s.to_uppercase() // UPPERCASE
s.to_lowercase() // lowercase
s.trim() // Remove whitespace
s.split(" ") // Split iterator
// String concatenation
let s1 = String::from("Hello, ");
let s2 = String::from("world!");
let s3 = s1 + &s2; // s1 is moved
let s4 = format!("{}{}", s3, "!"); // format! macro
// Converting
let num: i32 = "42".parse().unwrap();
let s = 42.to_string();
// Creating vectors
let v1: Vec<i32> = Vec::new();
let v2 = vec![1, 2, 3, 4, 5];
// Vector operations
let mut v = vec![1, 2, 3];
v.push(4); // Add to end
v.pop(); // Remove & return last
v.len(); // Length
v.is_empty(); // Check if empty
v.insert(0, 0); // Insert at index
v.remove(0); // Remove at index
// Accessing elements
let first = v[0]; // Panics if out of bounds
let first = v.get(0); // Returns Option<&T>
// Iteration
for item in &v {
println!("{}", item);
}
for item in &mut v {
*item += 1;
}
// Slicing
let slice = &v[1..3];
use std::collections::HashMap;
// Creating HashMaps
let mut map: HashMap<String, i32> = HashMap::new();
let map2: HashMap<_, _> = vec![("a", 1), ("b", 2)]
.into_iter().collect();
// HashMap operations
map.insert(String::from("key"), 10);
map.get("key"); // Returns Option<&V>
map.remove("key"); // Remove entry
map.contains_key("key"); // Check key exists
map.len(); // Number of entries
// Entry API
map.entry(String::from("key")).or_insert(0);
// Iteration
for (key, value) in &map {
println!("{}: {}", key, value);
}
// Basic function
fn greet(name: &str) -> String {
format!("Hello, {}!", name) // No semicolon = return
}
// Multiple parameters
fn add(a: i32, b: i32) -> i32 {
a + b
}
// No return value (returns unit type)
fn print_value(x: i32) {
println!("{}", x);
}
// Early return
fn check(x: i32) -> bool {
if x < 0 {
return false;
}
true
}
// Closures
let add_one = |x: i32| x + 1;
let add = |a, b| a + b;
let print = |x| println!("{}", x);
// Closure capturing
let multiplier = 3;
let multiply = |x| x * multiplier;
// If/else
if condition {
// ...
} else if other {
// ...
} else {
// ...
}
// If as expression
let result = if condition { "yes" } else { "no" };
// Loop (infinite)
loop {
break; // Exit loop
}
// Loop with return value
let result = loop {
break 42;
};
// While loop
while condition {
// ...
}
// For loop
for i in 0..5 { // 0, 1, 2, 3, 4
println!("{}", i);
}
for i in 0..=5 { // 0, 1, 2, 3, 4, 5
println!("{}", i);
}
for (i, val) in v.iter().enumerate() {
println!("{}: {}", i, val);
}
// Match expression
let x = 1;
match x {
1 => println!("one"),
2 | 3 => println!("two or three"),
4..=10 => println!("four to ten"),
_ => println!("other"),
}
// Match with binding
match x {
n @ 1..=5 => println!("got {}", n),
_ => (),
}
// Match on Option
match some_option {
Some(value) => println!("{}", value),
None => println!("nothing"),
}
// Match on Result
match some_result {
Ok(value) => println!("{}", value),
Err(e) => println!("Error: {}", e),
}
// If let (shorthand for single match)
if let Some(value) = some_option {
println!("{}", value);
}
// While let
while let Some(value) = stack.pop() {
println!("{}", value);
}
// Ownership - each value has one owner
let s1 = String::from("hello");
let s2 = s1; // s1 is moved, no longer valid
// println!("{}", s1); // Error!
// Clone (deep copy)
let s1 = String::from("hello");
let s2 = s1.clone(); // Both valid
// References (borrowing)
let s = String::from("hello");
let len = calculate_length(&s); // Borrow
println!("{}", s); // Still valid
fn calculate_length(s: &String) -> usize {
s.len()
}
// Mutable references (only one at a time)
let mut s = String::from("hello");
change(&mut s);
fn change(s: &mut String) {
s.push_str(", world");
}
// Slices (references to part of data)
let s = String::from("hello world");
let hello = &s[0..5]; // &str slice
let world = &s[6..11];
// Defining a struct
struct User {
username: String,
email: String,
active: bool,
}
// Creating instances
let user = User {
username: String::from("alice"),
email: String::from("alice@example.com"),
active: true,
};
// Shorthand (when variable names match)
let username = String::from("bob");
let user = User { username, ..user }; // Struct update syntax
// Tuple structs
struct Point(i32, i32, i32);
let origin = Point(0, 0, 0);
// Unit-like structs
struct AlwaysEqual;
// Methods with impl
impl User {
// Associated function (constructor)
fn new(username: String, email: String) -> Self {
Self { username, email, active: true }
}
// Method (takes &self)
fn is_active(&self) -> bool {
self.active
}
// Mutable method
fn deactivate(&mut self) {
self.active = false;
}
}
// Basic enum
enum Direction {
North,
South,
East,
West,
}
// Enum with data
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
// Using enums
let msg = Message::Write(String::from("hello"));
// Option enum (built-in)
let some_number: Option<i32> = Some(5);
let no_number: Option<i32> = None;
// Result enum (built-in)
let success: Result<i32, String> = Ok(42);
let failure: Result<i32, String> = Err("error".to_string());
// Impl on enums
impl Message {
fn call(&self) {
// ...
}
}
// Result type
fn read_file() -> Result<String, std::io::Error> {
std::fs::read_to_string("file.txt")
}
// Handling Results
let content = read_file().unwrap(); // Panic on error
let content = read_file().expect("Failed"); // Panic with message
let content = read_file().unwrap_or_default(); // Default on error
// Match on Result
match read_file() {
Ok(content) => println!("{}", content),
Err(e) => println!("Error: {}", e),
}
// ? operator (propagate errors)
fn process() -> Result<String, std::io::Error> {
let content = std::fs::read_to_string("file.txt")?;
Ok(content.to_uppercase())
}
// Multiple error types
use std::error::Error;
fn run() -> Result<(), Box<dyn Error>> {
let content = std::fs::read_to_string("file.txt")?;
let num: i32 = content.trim().parse()?;
Ok(())
}
// Defining a trait
trait Summary {
fn summarize(&self) -> String;
// Default implementation
fn preview(&self) -> String {
format!("Read more: {}", self.summarize())
}
}
// Implementing a trait
struct Article { title: String, content: String }
impl Summary for Article {
fn summarize(&self) -> String {
format!("{}: {}", self.title, self.content)
}
}
// Trait bounds
fn notify<T: Summary>(item: &T) {
println!("Breaking: {}", item.summarize());
}
// Multiple trait bounds
fn process<T: Summary + Clone>(item: &T) { }
// Where clause
fn some_fn<T, U>(t: &T, u: &U)
where
T: Summary + Clone,
U: Clone + std::fmt::Debug,
{ }
// Returning traits
fn create() -> impl Summary {
Article { title: "...".to_string(), content: "...".to_string() }
}
// Generic function
fn largest<T: PartialOrd>(list: &[T]) -> &T {
let mut largest = &list[0];
for item in list {
if item > largest {
largest = item;
}
}
largest
}
// Generic struct
struct Point<T> {
x: T,
y: T,
}
// Generic impl
impl<T> Point<T> {
fn x(&self) -> &T {
&self.x
}
}
// Specific impl
impl Point<f64> {
fn distance(&self) -> f64 {
(self.x.powi(2) + self.y.powi(2)).sqrt()
}
}
// Generic enum
enum Option<T> {
Some(T),
None,
}
let v = vec![1, 2, 3, 4, 5];
// Iterator methods
v.iter() // Iterate over &T
v.iter_mut() // Iterate over &mut T
v.into_iter() // Take ownership
// Common iterator adaptors
v.iter().map(|x| x * 2)
v.iter().filter(|x| **x > 2)
v.iter().enumerate()
v.iter().take(3)
v.iter().skip(2)
v.iter().zip(other.iter())
v.iter().chain(other.iter())
v.iter().flatten()
v.iter().rev()
// Consuming adaptors
v.iter().collect::<Vec<_>>()
v.iter().sum::<i32>()
v.iter().fold(0, |acc, x| acc + x)
v.iter().any(|x| *x > 3)
v.iter().all(|x| *x > 0)
v.iter().find(|x| **x > 3)
v.iter().count()
// Printing
println!("Hello, {}!", name); // Print with newline
print!("No newline"); // Print without newline
eprintln!("Error!"); // Print to stderr
dbg!(expression); // Debug print
// Formatting
format!("Hello, {}!", name); // Return String
// Vector creation
vec![1, 2, 3];
// Assertions
assert!(condition);
assert_eq!(a, b);
assert_ne!(a, b);
// Panic
panic!("Something went wrong!");
todo!(); // Placeholder
unimplemented!(); // Not implemented
unreachable!(); // Should never reach
// Derive macros
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct MyStruct { }
// Lifetime annotation
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
// Struct with lifetime
struct Excerpt<'a> {
part: &'a str,
}
impl<'a> Excerpt<'a> {
fn level(&self) -> i32 {
3
}
}
// Static lifetime (lives for entire program)
let s: &'static str = "I live forever";