Below is a Rust Programming Cheat Sheet packed with detailed explanations and definitions for each section. I’ve put this together to help you really get a handle on Rust’s core ideas—whether you’re learning, need a quick reference, or prepping for an interview. Let’s explore Rust’s world together!
Table of Contents
Rust Programming Revise
Variables & Data Types
Rust is a statically typed language, which means it figures out all the variable types when you compile your code. This keeps things safe and speedy.
rust
fn main() {
let x = 5; // Immutable variable (default)
let mut y = 10; // Mutable variable (can be changed)
const PI: f64 = 3.1415; // Constant (compile-time, type required)
// Common Data Types
let a: i32 = -10; // Signed 32-bit integer
let b: u32 = 100; // Unsigned 32-bit integer
let c: f64 = 3.14; // 64-bit floating point
let d: bool = true; // Boolean
let e: char = 'A'; // Unicode character
println!("{} {} {} {} {}", a, b, c, d, e);
}
- Definition: Variables: These are how you store data. By default, you use let to make them immutable (locked in place), or let mut if you want to change them later. Constants, marked with const, are set-in-stone values worked out at compile time.
- Explanation:
- Immutability: let x = 5; can’t be reassigned unless marked mut. This prevents accidental changes.
- Types: You’ll see i32 for signed integers (positive or negative), u32 for unsigned ones (positive only), f64 for floating-point numbers, bool for true/false, and char for single Unicode characters.
- Constants: const requires a type annotation and is immutable by design.
Strings
Rust gives you two ways to work with strings: String (which you own and can change) and &str (a borrowed, read-only slice).
rust
fn main() {
let s1 = String::from("Hello, Rust!"); // Owned, heap-allocated
let s2 = "Hello, world!"; // String slice, static
let mut s3 = String::from("Rust"); // Mutable String
s3.push_str(" Programming"); // Append to String
println!("{}", s1);
println!("{}", s2);
println!("{}", s3);
}
- Definition: String is a growable, heap-allocated string, while &str is a fixed, immutable view into string data.
- Explanation:
- String: Created with String::from(), owns its data, and can be modified (e.g., push_str()).
- &str: A lightweight reference to a string, often used for static text or function parameters.
- Use Case: Use &str for read-only, String when you need ownership or mutability.
Functions
Functions are like little reusable packets of code. They take in parameters with specific types and can spit out a return value.
rust
fn add(a: i32, b: i32) -> i32 {
a + b // Implicit return (no semicolon)
}
fn main() {
let result = add(10, 20);
println!("Sum: {}", result);
}
- Definition: Functions are declared with fn, take typed arguments, and specify a return type with ->.
- Explanation:
- Implicit Return: The last expression without a ; is returned automatically.
- Type Safety: You’ve got to tell Rust exactly what types your parameters and return values are—no guessing allowed!
- Purpose: They bundle up your logic so your code stays neat, modular, and easy to reuse.
Control Flow
Rust provides if-else for conditionals and match for pattern matching.
rust
fn main() {
let num = 5;
// If-Else
if num > 0 {
println!("Positive");
} else {
println!("Negative or Zero");
}
// Match
let day = 3;
match day {
1 => println!("Monday"),
2 => println!("Tuesday"),
_ => println!("Other day"), // Wildcard (default)
}
}
- Definition: Control flow directs program execution based on conditions (if-else) or patterns (match).
- Explanation:
- if-else: Standard conditional logic, can also be an expression (e.g., let x = if num > 0 { 1 } else { 0 };).
- match: Exhaustive pattern matching, requiring all cases to be handled (or use _ for default). More powerful than switch-case.
Loops
Rust offers loop (infinite), while (conditional), and for (range-based).
rust
fn main() {
// Infinite Loop
let mut count = 0;
loop {
count += 1;
if count == 5 {
break; // Exit loop
}
}
// While Loop
let mut n = 0;
while n < 5 {
println!("{}", n);
n += 1;
}
// For Loop
for i in 1..=5 { // Inclusive range (1 to 5)
println!("Number: {}", i);
}
}
- Definition: Loops repeat code execution: loop runs forever, while runs while a condition holds, for iterates over a range.
- Explanation:
- loop: Useful for manual control with break or continue.
- while: Traditional condition-based looping.
- for: Iterates over ranges (e.g., 1..5 excludes 5, 1..=5 includes 5), safe and concise.
Structs (Custom Data Types)
Structs define custom types with named fields.
rust
struct Person {
name: String,
age: u8,
}
fn main() {
let p1 = Person {
name: String::from("Alice"),
age: 30,
};
println!("{} is {} years old.", p1.name, p1.age);
}
- Definition: A struct is a user-defined type that groups related data.
- Explanation:
- Fields: Can hold any type (e.g., String, u8).
- Ownership: Structs own their data, and moving them transfers ownership.
- Use Case: Models real-world entities (e.g., a person, car).
Enums (Enumeration)
Enums define a type with a fixed set of variants.
rust
enum Color {
Red,
Green,
Blue,
}
fn main() {
let c = Color::Red;
match c {
Color::Red => println!("Red color"),
Color::Green => println!("Green color"),
Color::Blue => println!("Blue color"),
}
}
- Definition: An enum represents a value that can be one of several variants.
- Explanation:
- Variants: Simple names (e.g., Red) or can hold data (e.g., Message(String)).
- Pattern Matching: Paired with match for handling each variant.
- Use Case: Represents states or options (e.g., colors, statuses).
Option & Result Types
Option handles optional values, Result handles success or failure.
rust
fn divide(a: i32, b: i32) -> Option<i32> {
if b == 0 {
None
} else {
Some(a / b)
}
}
fn divide_result(a: i32, b: i32) -> Result<i32, String> {
if b == 0 {
Err(String::from("Cannot divide by zero"))
} else {
Ok(a / b)
}
}
- Definition: Option<T> is Some(T) or None; Result<T, E> is Ok(T) or Err(E).
- Explanation:
- Option: Replaces null, forcing explicit handling of missing values.
- Result: Used for error handling without exceptions.
- Use Case: Option for nullable data, Result for operations that might fail.
Ownership & Borrowing
Rust’s memory management system ensures safety without a garbage collector.
rust
fn main() {
let s = String::from("Hello");
takes_ownership(s); // s is moved, no longer valid here
let num = 5;
makes_copy(num); // num is copied, still valid
}
fn takes_ownership(some_string: String) {
println!("{}", some_string);
}
fn makes_copy(some_integer: i32) {
println!("{}", some_integer);
}
- Definition: Ownership assigns each value a single owner; borrowing allows temporary access (& immutable, &mut mutable).
- Explanation:
- Move: Ownership transfers (e.g., String), invalidating the original variable.
- Copy: Simple types (e.g., i32) are copied instead of moved.
- Borrowing: Prevents data races by limiting mutable references.
Concurrency (Threads)
Rust supports safe concurrency with threads.
rust
use std::thread;
use std::time::Duration;
fn main() {
let handle = thread::spawn(|| {
for i in 1..5 {
println!("From thread: {}", i);
thread::sleep(Duration::from_millis(500));
}
});
handle.join().unwrap(); // Wait for thread to finish
}
- Definition: Threads run code concurrently, created with thread::spawn().
- Explanation:
- Safety: Ownership ensures no data races (data must be moved or safely shared).
- join(): Synchronizes threads, waiting for completion.
- Use Case: Parallel tasks (e.g., processing data in parallel).
File Handling
Rust also has handy tools for dealing with files—reading and writing them—complete with built-in error handling to keep things smooth.
rust
use std::fs::File;
use std::io::{self, Read, Write};
fn main() -> io::Result<()> {
let mut file = File::create("hello.txt")?;
file.write_all(b"Hello, Rust!")?;
let mut file = File::open("hello.txt")?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
println!("File Content: {}", contents);
Ok(())
}
- Definition: File operations use std::fs::File and std::io, returning Result for error handling.
- Explanation:
- File::create() / File::open(): Creates or opens files.
- ? Operator: Propagates errors, simplifying error handling.
- Use Case: Reading/writing persistent data (e.g., logs, configs).
Rust Programming Cheat Sheet Key Points for Interview.
Variables & Data Types
- What it is: Rust is statically typed, meaning types are checked at compile time. Variables are immutable by default (let), but can be made mutable (let mut). Constants (const) are immutable and require explicit type annotation.
- Key Points for Interview:
- Immutability by Default: Rust encourages safety by making variables immutable unless explicitly marked mut. This prevents accidental changes.
- Common Types: Rust has some go-to data types you’ll see a lot: i32 (a 32-bit integer that can be positive or negative), u32 (unsigned, so only positive numbers), f64 (a 64-bit floating-point number for decimals), bool (just true or false), and char (a single Unicode character, like ‘A’ or ‘π’).
- Constants vs Variables: const is evaluated at compile time and cannot change, while let variables can be mutable with mut.
- Here’s a classic interview question: “Why does Rust make immutability the default?”
- Answer: Rust does this to keep your code safer. By locking variables unless you say otherwise, it stops sneaky bugs—like accidental changes—dead in their tracks. Plus, it makes reasoning about your code way easier, especially when you’re juggling multiple threads in concurrent setups.
Strings
- What it is: Rust has String (heap-allocated, growable) and &str (immutable string slice, usually a reference to static text).
- Key Points for Interview:
- String vs &str: String owns its data and can be modified (e.g., with push_str()), while &str is a view into string data and is immutable.
- Memory Management: String is heap-allocated and cleaned up when it goes out of scope (ownership rules apply), while &str is typically stack-based or references existing data.
- Interview Question: “When would you use &str over String?”
- Answer: Use &str for read-only string data (e.g., function parameters) because it’s lightweight and doesn’t require ownership. Use String when you need to own or modify the string.
Functions
- What it is: Functions are defined with fn, take typed parameters, and can return values with ->. Rust uses implicit returns (no semicolon) for the last expression.
- Key Points for Interview:
- Type Annotations: Parameters and return types must be explicitly typed, reflecting Rust’s static typing.
- Implicit Return: When it comes to functions, Rust has this neat trick called implicit return. If you skip the semicolon (;) at the end of an expression, that’s what the function hands back—super clean and to the point.
- Interview Question: ” How does Rust deal with function returns compared to something like C?”
Control Flow
- What it is: Rust provides if-else for conditionals and match for pattern matching, which is more powerful than traditional switch-case.
- Key Points for Interview:
- if-else: Simple and similar to other languages, but can be used as an expression (e.g., let x = if condition { 5 } else { 10 };).
- match: Exhaustive (must cover all cases or use _), making it safer than switch-case by preventing unhandled scenarios.
- Interview Question: “Why is match preferred over if-else in Rust?”
- Answer: match ensures all possibilities are handled (compile-time check), reducing runtime errors, and is more expressive for complex pattern matching.
Loops
- What it is: Rust has loop (infinite), while (conditional), and for (range-based iteration).
- Key Points for Interview:
- loop: Useful for infinite loops, often paired with break for control.
- for with Ranges: 1..5 (exclusive) vs 1..=5 (inclusive) is concise and common for iteration.
- Safety: No manual index management like in C, reducing off-by-one errors.
- Interview Question: “How does Rust’s for loop differ from C’s?”
- Answer: Rust’s for iterates over ranges or collections directly (e.g., for i in 1..5), avoiding manual indexing and making it safer and more ergonomic.
Structs (Custom Data Types)
- What it is: Structs define custom data types with named fields, similar to classes but without methods by default.
- Key Points for Interview:
- Ownership: Structs own their fields, and moving a struct moves all its data.
- Instantiation: Use the StructName { field: value, .. } syntax.
- Interview Question: “How do structs relate to Rust’s ownership model?”
- Answer: When a struct is passed to a function, its ownership is transferred unless borrowed (&), aligning with Rust’s memory safety guarantees.
Enums (Enumeration)
- What it is: Enums define a type with a fixed set of variants, often used with match.
- Key Points for Interview:
- Variants: Simple (e.g., Red, Green) or complex (e.g., Message(String)).
- Pattern Matching: Enums shine with match, enabling robust logic for each variant.
- Interview Question: “How are enums in Rust different from C?”
- Answer: Rust enums can hold data (e.g., Option<i32>), making them more powerful than C’s simple integer-based enums.
Option & Result Types
- What it is: Option<T> handles nullable values (Some or None), while Result<T, E> handles success (Ok) or failure (Err).
- Key Points for Interview:
- Option: Replaces null pointers, forcing explicit handling of absence.
- Result: Encourages error handling without exceptions, using match or ?.
- Interview Question: “Why does Rust use Option instead of null?”
- Answer: Null leads to runtime errors (e.g., null pointer dereference). Option ensures the absence of a value is handled at compile time, improving safety.
Ownership & Borrowing
- What it is: Rust’s core memory management system: each value has an owner, and there can only be one mutable reference (&mut) or multiple immutable references (&) at a time.
- Key Points for Interview:
- Ownership Rules: Move, copy, or borrow. Types like i32 copy, while String moves.
- Borrowing: & for immutable, &mut for mutable, with strict rules to prevent data races.
- Interview Question: “What problem does ownership solve?”
- Answer: It eliminates manual memory management (like malloc/free) and prevents issues like dangling pointers or double frees, all at compile time.
Concurrency (Threads)
- What it is: Rust uses threads for parallelism, with thread::spawn() creating new threads and join() synchronizing them.
- Key Points for Interview:
- Safety: Ownership ensures no data races—only owned or safely borrowed data can be passed to threads.
- move Keyword: Transfers ownership to a thread closure if needed.
- Interview Question: “How does Rust prevent data races in concurrency?”
- Answer: Compile-time ownership and borrowing rules ensure that data is either moved or immutably shared, eliminating races without runtime overhead.
File Handling
- What it is: Rust provides std::fs::File and std::io for reading/writing files, with error handling via Result.
- Key Points for Interview:
- ? Operator: Propagates errors concisely, reducing boilerplate.
- Safety: No unchecked file operations—errors must be handled.
- Interview Question: “How does Rust’s file handling differ from Python?”
- Answer: Rust requires explicit error handling with Result, while Python uses exceptions. Rust’s approach catches issues at compile time, improving reliability.
Why Rust?
- What It Means: It’s all about its killer combo of safety, performance, and concurrency. Think no garbage collector eating up resources and zero-cost abstractions that don’t slow you down—pretty sweet, right?
- Safety: Rust is like a super-strict coach that catches mistakes before your code even runs. Think of bugs like using memory after it’s freed or accessing null pointers—Rust’s rules (like ownership) stop those at compile time, so you don’t crash later.
- Performance: It’s as fast as C or C++, but without the headaches. Rust skips the garbage collector (that thing in languages like Java that cleans up memory but slows you down). Instead, it uses its ownership system to handle memory efficiently—no extra runtime cost.
- Concurrency: Writing code that runs multiple tasks (threads) at once can be a nightmare—data races can ruin your day. Rust makes it safe by ensuring threads don’t step on each other’s toes, all checked at compile time.
- No Garbage Collector: No GC means no pauses or overhead—just raw speed.
- Zero-Cost Abstractions: Rust gives you fancy features (like iterators or pattern matching) without slowing your program down. You get the power of high-level languages, but it compiles to lean, mean machine code.
- Why It Matters: These traits make Rust perfect for systems programming—like building operating systems, browsers, or game engines—where you need control, speed, and reliability all at once.
- How to Explain It in an Interview: “Rust’s my go-to because it nails safety, performance, and concurrency. It catches bugs like memory errors before they bite, runs crazy fast without a garbage collector, and lets me write concurrent code without sweating data races. Plus, stuff like zero-cost abstractions means I can write clean, powerful code that doesn’t sacrifice speed—it’s the best of both worlds!”