Author: 0xRayaa
Edition: Rust 2021
Tested with: rustc 1.78.0 (stable)
π Note from 0xRayaa :
These are the notes I originally made while learning Rust programming.
You can check out my first version here: Rust Programming β Notion Notes.
I later refined, reorganized, and expanded them into this version so that others can learn from it too.
I hope it helps someone the same way it helped me while studying.
- 1. Cargo Installation
- 2. Variables
- 3. Data Types
- 4. Ownership & Borrowing
- 5. Structs
- 6. Enums & Pattern Matching
- 7. Modules, Packages & Crates
- 8. Common Collections
- 9. Error Handling
- 10. Generics & Traits
- 11. Lifetimes
- 12. Testing in Rust
- π― Final Notes & Resources
Install Rust using rustup:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shThen verify:
rustc --version
cargo --versionMutable and Immutable
fn main() {
let x = 5; // immutable
let mut y = 10; // mutable
y += 1;
println!("x = {}, y = {}", x, y);
}Constants
const MAX_POINTS: u32 = 100_000;- Const items are compile-time constants (inlined wherever used).
- They cannot be mutable (mut is not allowed).
- For global mutable state, use static mut (unsafe).
Scalar Types (single values)
| Type | Default | Example | Notes |
|-----------|---------|------------------|--------------------------|
| Integer | i32 | let x = 42; | Signed 32-bit by default |
| Float | f64 | let pi = 3.14; | 64-bit precision |
| Boolean | bool | let t = true; | Logical true/false |
| Character | char | let c = 'π¦'; | Unicode scalar |Integer Overflow β’ Debug mode: causes panic on overflow. β’ Release mode: wraps around (e.g., 256 β 0).
Compound Types
- Tuple: fixed-size, possibly mixed types
let tup: (i32, f64, u8) = (500, 6.4, 1); let (x, y, z) = tup;
- Array: same type, fixed length
let arr = [1, 2, 3, 4];
- Slice: reference view into array
let slice = &arr[1..3];
Rust enforces memory safety without garbage collection using ownership.
Move Semantics
fn main() {
let s1 = String::from("hello");
let s2 = s1; // s1 moved to s2
// println!("{}", s1); // β compile error: s1 moved
println!("{}", s2);
}Copy Semantics Copy types (e.g., integers, bool, char) are duplicated on assignment.
fn main() {
let x = 5;
let y = x; // both valid
println!("x = {}, y = {}", x, y);
}Cloning
let s1 = String::from("hello");
let s2 = s1.clone(); // deep copy
println!("s1 = {}, s2 = {}", s1, s2);Functions Taking Ownership
fn main() {
let s = String::from("hello");
takes_ownership(s);
// println!("{}", s); // β s moved
}
fn takes_ownership(some_string: String) {
println!("{}", some_string);
}Functions Returning Ownership
fn gives_ownership() -> String {
let s = String::from("hello");
s
}
fn main() {
let s = gives_ownership();
println!("{}", s);
}Borrowing (References)
fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1); // borrow
println!("Length of '{}' is {}", s1, len);
}
fn calculate_length(s: &String) -> usize {
s.len()
}Rules of References β’ At any time: β€ either one mutable reference (&mut T) β€ or any number of immutable references (&T) β’ References must always be valid (no dangling refs).
struct User {
username: String,
active: bool,
}
impl User {
fn deactivate(&mut self) {
self.active = false;
}
}
fn main() {
let mut user = User { username: "alice".into(), active: true };
user.deactivate();
println!("User {} active: {}", user.username, user.active);
}- Making struct mutable (mut user) makes all its fields mutable.
- Methods take &self (borrowed) or &mut self for mutable methods.
- Associated functions (no self) are defined with impl.
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
}
fn process_message(msg: Message) {
match msg {
Message::Quit => println!("Quit message"),
Message::Move { x, y } => println!("Move to ({}, {})", x, y),
Message::Write(text) => println!("Message: {}", text),
}
}Option Enum
Rust has no null. Instead: let x: Option = Some(5); let y: Option = None;
if let Some(val) = x {
println!("Value = {}", val);
}Avoid unwrap() on user input β it can panic. Use pattern matching or unwrap_or().
- Package: contains a Cargo.toml and manages one or more crates.
- Crate: compilation unit (binary or library). β’ src/main.rs β binary crate β’ src/lib.rs β library crate β’ src/bin/*.rs β additional binaries
Example :
my_project/
βββ Cargo.toml
βββ src/
βββ main.rs
βββ lib.rs
βββ bin/
βββ helper.rsVectors
let mut v = vec![1, 2, 3];
v.push(4);
for i in &v {
println!("{}", i);
}Strings
let mut s = String::from("Hello");
s.push_str(", world!");HashMaps
use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Red"), 50);Panic
Used for unrecoverable errors.
panic!("Critical failure!");Result Enum
Used for recoverable errors.
use std::fs::File;
fn main() {
let f = File::open("hello.txt");
match f {
Ok(file) => println!("File opened successfully!"),
Err(e) => println!("Error: {}", e),
}
}The ? Operator
use std::error::Error;
use std::fs::File;
use std::io::Read;
fn read_file() -> Result<String, Box<dyn Error>> {
let mut file = File::open("hello.txt")?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
Ok(contents)
}When to Panic vs Result β’ Use panic! when recovery is impossible or program state is invalid. β’ Use Result for recoverable or expected failures (file missing, invalid input, etc.).
Custom Validation Example
pub struct Guess {
value: i32,
}
impl Guess {
pub fn new(value: i32) -> Result<Guess, String> {
if value < 1 || value > 100 {
Err(String::from("Guess value must be between 1 and 100."))
} else {
Ok(Guess { value })
}
}
}Generics
fn largest<T: PartialOrd + Copy>(list: &[T]) -> T {
let mut largest = list[0];
for &item in list {
if item > largest {
largest = item;
}
}
largest
}Traits
trait Summary {
fn summarize(&self) -> String;
}
struct Article {
title: String,
author: String,
}
impl Summary for Article {
fn summarize(&self) -> String {
format!("{} by {}", self.title, self.author)
}
}
fn notify(item: &impl Summary) {
println!("Breaking news! {}", item.summarize());
}Dangling Reference Example
// β Invalid
fn main() {
let r;
{
let x = 5;
r = &x;
}
// println!("{}", r); // error: x does not live long enough
}Corrected Version
fn main() {
let x = 5;
let r = &x;
println!("{}", r);
}Lifetime Annotations Example
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}'static Lifetime
let s: &'static str = "I live forever";String literals have 'static lifetime.
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
#[test]
fn not_equal() {
assert_ne!(5, 3);
}
}Run tests:
cargo testTip: Left/right order in assert_eq! is not enforced, but be consistent: use (actual, expected) for clarity.
β
Run cargo fmt and cargo clippy before committing code.
β
Avoid using unwrap() and expect() on dynamic input.
β
Prefer Result + ? for error propagation.
β
Use #[derive(Debug)] for easier debugging and cleaner print outputs.
- π The Rust Programming Language (Official Book)
- π¦ Rust By Example
- π‘ Rust Playground
- π Clippy Lints
π¦ Built with β€οΈ by 0xRayaa β Happy Rusting!