Rust Learning Notes
This notes were taken while learning the Rust programming language, most of the notes are from the experimental Rust Book. Other sources maybe added later, the notes are separated by date taken.
2025-04-30
Rust Book - Chapter 1: Getting Started
On functions and Macros
// This is a comment
fn main() {
    println!("Hello, world!");
}
- fnis the keyword that defines a function.
- println!is a macro that prints to the console.
- !indicates that- printlnis a macro, not a function.
2025-05-02
Rust Book - Chapter 1: Getting Started - Cargo
On Cargo
cargo new <project_name> # Create a new Rust project
cargo build # Build the project
cargo run # Build & Run the project
cargo check # Build the project without producing an executable
cargo build --release # Build the project in release mode
- Cargo is the Rust package manager and build system.
- It helps manage dependencies, build packages, and run tests.
- The Cargo.tomlfile is where dependencies and package metadata are defined.
- Cargo stores the the resulting build executables in the targetdirectory.
2025-05-05
Rust Book - Chapter 3: Common Programming Concept - Variables and Mutability
On Variables and Mutability
fn main() {
    let x = 5;
    println!("The value of x is: {x}");
    x = 6;
    println!("The value of x is: {x}");
}
- In Rust, variables are immutable by default.
- This code will not compile because xis immutable by default.
- To make a variable mutable, we use the mutkeyword:
fn main() {
    let mut x = 5;
    println!("The value of x is: {x}");
    x = 6;
    println!("The value of x is: {x}");
}
- The mutkeyword makes the variable mutable, allowing us to change its value.
Constants
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
- constis the keyword used to declare constants instead of- let
- like immutable variables constants are never allowed to change
- constants can’t be made mutable by using the mutkeyword
- type annotation is required when declaring constants
- constants can only be set to constant expressions and not runtime computed results
Shadowing
fn main(){
    let x = 5;
    let x = x + 1;
    {
        let x = x * 2;
        println!("The value of x in the inner scope is: {x}");
    }
    println!("The value of x is: {x}");
    let spaces = "   ";
    let spaces = spaces.len();
}
- rust allows declaring a variable with the name of a previously declared variable this is know as shadowing
- when shadowing a previously declared variable, the type can be changed
2025-05-08
Rust Book - Chapter 3: Common Programming Concept - Data Types
On Data Types
let guess: u32 = "42".parse().expect("Not a number!");
converting String to a numeric type using parse requires explicit type annotation, the compiler can’t infer it if not provided and will error at compile time
- Rust is a statically typed language, this means that we must know the type of all variables at compile time
- The compiler can infer the data type, but in cases where type can’t be inferred then we must add type annotation like above
Scalar Types
Rust has for primary scalar types: integers, floats, booleans, and characters
Scalar Types - Integers
- integer types include u8,i8,u16,i16,u32,i32,u64,i64,u128,i128whereuis for unsigned andiis for signed
- type suffix can be used such as 57u8to represent 8 bit unsigned integer
- visual separators are allowed as well such as 1_525_200to represent1525200
- when compiling in debug mode, rust will check integer overflowandpanicat runtime but in release mode integers will overflow
Scalar Types - Floats
fn main() {
    let x = 2.0; // f64
    let y: f32 = 3.0; // f32
}
- in Rust all floating-point types are signed, and there are of two types f32andf64, the default type isf64
- floating point numbers are represented in IEEE-754standard. Thef32type is a single-precision 32-bit float andf64is a double-precision 64-bit float
Scalar Types - Numeric Operations
Rust supports all basics mathematical operations expected of the number types:
fn main() {
    // addition
    let sum = 5 + 10;
    // subtraction
    let difference = 95.5 - 4.3;
    // multiplication
    let product = 4 * 30;
    // division
    let quotient = 56.7 / 32.2;
    let truncated = -5 / 3;
    // remainder
    let remainder = 43 % 5;
}
Scalar Types - Booleans
As in most other programming languages, the boolean type in Rust is bool and can be either true or false
fn main(){
    let t = true;
    let f: bool = false; // with explicit type annotation
}
Scalar Types - Character Type
The char type is rust most primitive alphabetic type. example:
fn main(){
    let c = 'z';
    let z: char = 'ℤ'; // with explicit type annotation
    let heart_eyed_cat = '😻';
}
- charis a 4-byte Unicode scalar value, representing a single character
- charcan represent more than just ASCII characters
- single quotes are used to represent charliterals
Compound Types
Rust has two primitive compound types: tuples and arrays
Compound Types - Tuples
fn main(){
    let tup: (i32, f64, u8) = (500, 6.4, 1);
    let (x, y, z) = tup; // destructuring
    println!("The value of y is: {y}");
    println!("The value of z is: {z}");
    println!("The value of x is: {x}");
    let five_hundred = tup.0;
    let six_point_four = tup.1;
    let one = tup.2;
    // mutable tuple
    let mut x: (i32, i32) = (1, 2);
    x.0 = 0;
    x.1 += 5;
    println!("The value of x is: {x}");
}
- tuples are fixed-length and can contain different types
- tuples are created using parentheses ()
- tuples can be destructured to access their values
- tuples can be mutable
Compound Types - Arrays
fn main(){
    let a = [1, 2, 3, 4, 5];
    let months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
    let a: [i32; 5] = [1, 2, 3, 4, 5]; // with explicit type annotation
    let a = [3; 5]; // array of 5 elements, all initialized to 3
    // accessing array elements
    let first = a[0];
    let second = a[1];
}
- unlike tuples, must contain the same type
- arrays in rust are fixed-length
- arrays are created using square brackets []
- arrays are useful when you want your data to be allocated on the stack, instead of the heap
- arrays are most useful when you know the size of the data and it will not change
use std::io;
fn main() {
    let a = [1, 2, 3, 4, 5];
    println!("Enter an array index: ");
    let mut index = String::new();
    io::stdin()
        .read_line(&mut index)
        .expect("Failed to read line");
    let index: usize = index
        .trim()
        .parse()
        .expect("Index entered was not a number");
    
    let element = a[index];
    println!("The value of the element at index {index} is: {element}");
}