diff --git a/rust_session/assignments/student_registry_v1/.gitignore b/rust_session/assignments/student_registry_v1/.gitignore new file mode 100644 index 00000000..9f970225 --- /dev/null +++ b/rust_session/assignments/student_registry_v1/.gitignore @@ -0,0 +1 @@ +target/ \ No newline at end of file diff --git a/rust_session/assignments/student_registry_v1/Cargo.lock b/rust_session/assignments/student_registry_v1/Cargo.lock new file mode 100644 index 00000000..2938d67f --- /dev/null +++ b/rust_session/assignments/student_registry_v1/Cargo.lock @@ -0,0 +1,34 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "colored" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "student_registry" +version = "0.1.0" +dependencies = [ + "colored", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] diff --git a/rust_session/assignments/student_registry_v1/Cargo.toml b/rust_session/assignments/student_registry_v1/Cargo.toml new file mode 100644 index 00000000..f670b24e --- /dev/null +++ b/rust_session/assignments/student_registry_v1/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "student_registry" +version = "0.1.0" +edition = "2021" + +[dependencies] +colored = "3.1.1" + + diff --git a/rust_session/assignments/student_registry_v1/README.md b/rust_session/assignments/student_registry_v1/README.md new file mode 100644 index 00000000..9441b751 --- /dev/null +++ b/rust_session/assignments/student_registry_v1/README.md @@ -0,0 +1,104 @@ +# Rust Student Registry +### A beginner project covering: `struct` · `Vec` · `enum` · `Option` · `impl` + +```bash +// ============================================================ +// STUDENT REGISTRY — Beginner Rust Project +// Concepts covered: +// • struct — grouping related data +// • enum — a value that can be one of several variants +// • Vec — a growable list +// • Option — a value that may or may not exist +// • impl block — adding methods to a struct +// • pattern matching (match, if let) +// • ownership & borrowing in practice +// ============================================================ +``` + +--- + +## How to run + +### Step 1 — Install Rust (if you haven't yet) +```bash +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +``` +Then restart your terminal, or run: +```bash +source "$HOME/.cargo/env" +``` + +### Step 2 — Verify the install +```bash +rustc --version # e.g. rustc 1.78.0 +cargo --version # e.g. cargo 1.78.0 +``` + +### Step 3 — Run the project +```bash +# Clone / copy this folder, then: +cd student_registry + +cargo run # compiles + runs in one command +``` + +### Other useful commands +```bash +cargo build # compile only (output goes to ./target/debug/) +cargo check # fast type-check without producing a binary (great while learning) +cargo run --release # compile with full optimisations (faster binary) +``` + +--- + +## What the project teaches + +| Concept | Where to look in main.rs | +|---|---| +| `struct` | `Student` and `Registry` definitions (~line 30, 90) | +| `enum` | `Grade` definition (~line 14) | +| `impl` block | `impl Grade`, `impl Student`, `impl Registry` | +| `Vec` | `Registry.students` field; `push`, `retain`, `iter` | +| `Option` | `find_by_name`, `find_by_id` return types; `demo_option()` | +| `match` | `Grade::as_str`, `letter_grade`, search demos | +| `if let` | `update_score`, `summary`, `demo_option` | +| Ownership / borrowing | `&self` vs `&mut self` on every method | +| Iterator methods | `summary()` — `.map()`, `.sum()`, `.max_by()` | + +--- + +## Expected output (abridged) + +``` + ╔══════════════════════════════════════╗ + ║ RUST STUDENT REGISTRY v1.0 ║ + ║ struct · Vec · enum · Option · impl ║ + ╚══════════════════════════════════════╝ + + ┌─ 1. Adding students (struct + Vec::push) + ✅ Added: Kay (ID 1) + ✅ Added: Yusrah (ID 2) + ... + + ┌─ 3. Search by name (Option<&Student>) + Found → ID: 2 Grade: 2nd Year Score: 64 + 'Yaw Owusu' not found in registry. + + ┌─ 8. Summary (iterators: map, sum, max_by) + Total students : 4 + Average score : 71.5 + Top student : Esi Boateng (91.0) +``` + +--- + +## Project structure + +``` +student_registry/ +├── Cargo.toml ← project metadata & dependencies +└── src/ + └── main.rs ← all code lives here (heavily annotated) +``` + +Everything is in one file intentionally — beginners don't need to navigate modules yet. diff --git a/rust_session/assignments/student_registry_v1/notes/borrowing_deep_dive.md b/rust_session/assignments/student_registry_v1/notes/borrowing_deep_dive.md new file mode 100644 index 00000000..76411372 --- /dev/null +++ b/rust_session/assignments/student_registry_v1/notes/borrowing_deep_dive.md @@ -0,0 +1,167 @@ +# Borrowing Deep Dive +Borrowing is like lending something to a friend. + +--- + +## The core idea + +```rust +fn main() { + let book = String::from("Rust Programming"); + + read(&book); // lend the book to `read` + + // book is still yours after the function returns + println!("I still have: {}", book); +} + +fn read(b: &String) { // b is a borrowed reference — not the owner + println!("Reading: {}", b); +} // borrow ends here — nothing is dropped +``` + +You still own `book`. `read` just borrowed it temporarily. When `read` finishes, +the book comes back to you automatically. + +--- + +## Without borrowing — ownership moves away + +```rust +fn main() { + let book = String::from("Rust Programming"); + + read(book); // ownership MOVES into read — you no longer have it + + println!("{}", book); // ❌ compile error: book was moved +} + +fn read(b: String) { // b owns the book now + println!("Reading: {}", b); +} // b is dropped here — book is gone forever +``` + +This is why borrowing exists. Without `&`, the value moves in and never comes back. + +--- + +## Two kinds of borrow + +### 1. Immutable borrow `&T` — look but don't touch + +```rust +fn main() { + let score = 95; + + print_score(&score); // lend score for reading + + println!("score is still {}", score); // ✅ still usable +} + +fn print_score(s: &i32) { + println!("Score: {}", s); + // *s = 100; // ❌ can't change it — it's an immutable borrow +} +``` + +### 2. Mutable borrow `&mut T` — borrow AND change it + +```rust +fn main() { + let mut score = 95; // must be `mut` to lend mutably + + add_bonus(&mut score); // lend score for writing + + println!("new score: {}", score); // 100 +} + +fn add_bonus(s: &mut i32) { + *s += 5; // * means "go to what this points to and change it" +} +``` + +--- + +## The two rules Rust enforces + +### Rule 1 — as many readers as you like + +```rust +fn main() { + let name = String::from("Henry"); + + let r1 = &name; // borrow 1 — fine + let r2 = &name; // borrow 2 — also fine + let r3 = &name; // borrow 3 — still fine + + println!("{} {} {}", r1, r2, r3); // ✅ multiple readers are safe +} +``` + +Think of a book in a library — unlimited people can read the same book at the +same time because no one is changing it. + +### Rule 2 — only ONE writer, and no readers at the same time + +```rust +fn main() { + let mut name = String::from("Henry"); + + let r1 = &name; // immutable borrow + let r2 = &mut name; // ❌ compile error — can't have &mut while &name exists + + println!("{} {}", r1, r2); +} +``` + +```rust +fn main() { + let mut name = String::from("Henry"); + + let r1 = &name; + println!("{}", r1); // r1 last used here — borrow ends + + let r2 = &mut name; // ✅ fine now — r1 is done + r2.push_str(" Osei"); + println!("{}", r2); // "Henry Osei" +} +``` + +Think of a whiteboard — unlimited people can read it, but the moment someone +starts writing on it, everyone else has to step back. + +--- + +## In the student registry + +```rust +// &mut self — we need to CHANGE the registry (push a new student) +pub fn add(&mut self, name: &str, ...) { + // ^^^ + // mutable borrow — we will modify self.students + + self.students.push(student); // this is the change +} + +// &self — we only READ the registry (print students) +pub fn list_all(&self) { + // ^^^^^ + // immutable borrow — we never change anything + + for student in &self.students { // borrow each student for printing + println!("{}", student.name); + } +} +``` + +--- + +## One-line mental model + +``` +&T → "I want to look at it" +&mut T → "I want to look at it AND change it" +T → "I want to own it and take it with me" +``` + +Rust checks these rules at compile time — zero runtime cost, zero crashes. diff --git a/rust_session/assignments/student_registry_v1/notes/core_logic.md b/rust_session/assignments/student_registry_v1/notes/core_logic.md new file mode 100644 index 00000000..5383ef4e --- /dev/null +++ b/rust_session/assignments/student_registry_v1/notes/core_logic.md @@ -0,0 +1,315 @@ +# Ownership, Borrowing & Referencing in the Student Registry + +This document walks through every place ownership, borrowing, and referencing +appear in the four files of the student registry project — +`grade.rs`, `student_struct.rs`, `registry.rs`, and `main.rs` — +and explains _why_ each one is used. + +--- + +## Quick reminder: the three concepts + +| Concept | Syntax | Meaning | +| -------------------------------- | --------------- | ------------------------------------------------------------------------------------------- | +| **Ownership** | `let x = value` | One variable is responsible for the value. When it goes out of scope, the value is dropped. | +| **Mutable borrow** | `&mut T` | Temporarily lend the value for reading AND writing. No ownership transfer. | +| **Immutable borrow / reference** | `&T` | Temporarily lend the value for reading only. No ownership transfer. | + +--- + +## 1. `grade.rs` + +```rust +pub fn as_str(&self) -> &str { + match self { + Grade::First => "1st Year", + Grade::Second => "2nd Year", + Grade::Third => "3rd Year", + } +} +``` + +### `&self` — immutable borrow of Grade + +`as_str` only needs to _read_ the variant — it never changes it. +`&self` is an immutable reference to the `Grade` value. + +- The caller keeps ownership of the `Grade`. +- `as_str` borrows it just long enough to run the `match` and return a string. +- When `as_str` returns, the borrow ends. The caller still owns the `Grade`. + +### `-> &str` — returning a reference to static memory + +The returned `&str` points to string literals like `"1st Year"` which live in +the program's static memory (baked into the binary). No heap allocation occurs. +The reference is valid for the entire lifetime of the program. + +--- + +## 2. `student_struct.rs` + +```rust +use crate::grade::Grade; + +pub struct Student { + pub id: u32, + pub name: String, + pub age: u8, + pub grade: Grade, + pub score: f32, +} + +impl Student { + pub fn new(id: u32, name: String, age: u8, grade: Grade, score: f32) -> Student { + Student { id, name, age, grade, score } + } +} +``` + +### `fn new(…) -> Student` — ownership transfer via return value + +`new` takes all five arguments **by value** — it takes ownership of each one: + +| Parameter | Type | What happens | +| --------- | -------- | ------------------------------------------------------ | +| `id` | `u32` | Copied (primitive, `Copy` trait) | +| `name` | `String` | **Moved** into the struct — heap memory is transferred | +| `age` | `u8` | Copied (primitive) | +| `grade` | `Grade` | **Moved** into the struct — enum value transferred | +| `score` | `f32` | Copied (primitive) | + +When `new` returns the `Student`, ownership of the entire struct — including +the `String` on the heap — moves to whoever called `new`. + +### Why `String` and not `&str`? + +`String` is an _owned_, heap-allocated string. The struct needs to _own_ its +`name` field so the name lives as long as the `Student` does. If we used `&str` +(a borrowed reference), we would have to track where the original string lives +and guarantee it outlives the struct — that is lifetime annotation territory, +which is more advanced. + +--- + +## 3. `registry.rs` + +```rust +pub struct Registry { + pub students: Vec, + next_id: u32, +} +``` + +### `Vec` — Registry owns all students + +`Registry` owns the `Vec`, and the `Vec` owns every `Student` inside it. +This is a chain of ownership: + +``` +Registry + └── Vec (heap) + ├── Student { id, name, age, grade, score } + ├── Student { … } + └── Student { … } +``` + +When `Registry` is dropped, the `Vec` is dropped, which drops every `Student`, +which drops every `String` inside each student. One owner, one cleanup — no +manual `free()` needed. + +--- + +### `pub fn new() -> Registry` + +```rust +pub fn new() -> Registry { + Registry { + students: Vec::new(), + next_id: 1, + } +} +``` + +Returns an owned `Registry` by value. The caller takes ownership. +`Vec::new()` creates an empty `Vec` — no heap allocation yet. +The heap only gets used when the first `push()` happens. + +--- + +### `pub fn add(&mut self, name: &str, age: u8, grade: Grade, score: f32)` + +```rust +pub fn add(&mut self, name: &str, age: u8, grade: Grade, score: f32) { + let id = self.next_id; + let student = Student::new(id, name.to_string(), age, grade, score); + println!(" ✅ Added: {} (ID {})", student.name, student.id); + self.students.push(student); + self.next_id += 1; +} +``` + +There are three distinct ownership/borrowing events here: + +#### `&mut self` — mutable borrow of Registry + +`add` needs to _change_ the registry — it pushes a new student and increments +`next_id`. So it takes a **mutable borrow** of the whole `Registry`. + +- The caller keeps ownership of the `Registry`. +- `add` can read and write any field on it. +- Only one `&mut` borrow can exist at a time — Rust enforces this. + +#### `name: &str` — immutable reference to a string + +`name` comes in as `&str` — a **borrowed reference** to a string slice. +`add` does not take ownership of the string. It only needs to read the +characters long enough to call `name.to_string()`. + +`name.to_string()` creates a brand-new owned `String` on the heap — that +owned copy is what gets moved into `Student::new`. The original string the +caller passed in is untouched and still owned by the caller. + +#### `grade: Grade` — ownership moves in + +`grade` is passed **by value** — ownership moves from the caller into `add`, +then immediately into `Student::new`, then into the `Student` struct. +After `reg.add(…, Grade::First, …)` in `main.rs`, the `Grade::First` value +lives inside the `Student` inside the `Vec`. The caller no longer holds it. + +#### `self.students.push(student)` — Vec takes ownership of Student + +After `println!`, `student` is moved into the `Vec` via `push`. From this +point: + +- `student` is no longer accessible as a local variable. +- The `Vec` is the owner. +- The memory for the student (including its `String name` on the heap) will be + freed only when the `Vec` itself is dropped. + +--- + +### `pub fn list_all(&self)` + +```rust +pub fn list_all(&self) { + for student in &self.students { + println!( + " {:>5} {:<20} {:>6} {:<10} {:.1}", + student.id, + student.name, + student.age, + student.grade.as_str(), + student.score, + ); + } +} +``` + +#### `&self` — immutable borrow of Registry + +`list_all` only reads — it never changes anything. `&self` is the correct +choice. The caller keeps ownership of the `Registry` and can use it again +after `list_all` returns. + +#### `for student in &self.students` — borrowing each element + +The `&` before `self.students` means we are iterating over **references** to +each `Student` — not moving them out of the `Vec`. + +- `student` inside the loop is `&Student` — a borrowed reference. +- The `Vec` still owns every student. +- Reading `student.id`, `student.name`, `student.age`, `student.score` is + fine because we are borrowing those fields through the reference. + +#### `student.grade.as_str()` — chained borrow + +`student` is `&Student`, so `student.grade` is accessing the `Grade` field +through a reference. Rust auto-derefs this for us. +`as_str` then takes `&self` on the `Grade` — another immutable borrow layered +on top. Both borrows exist only for the duration of the `println!` line, then +they end. + +--- + +## 4. `main.rs` + +```rust +fn main() { + let mut reg = Registry::new(); + + reg.add("Henry Osei", 20, Grade::First, 78.5); + reg.add("Kofi Mensah", 22, Grade::Second, 64.0); + reg.add("Esi Boateng", 21, Grade::First, 91.0); + + reg.list_all(); +} +``` + +### `let mut reg` — main owns Registry + +`main` owns `reg`. It must be `mut` because `add` takes `&mut self` — you +cannot mutably borrow something that was not declared mutable. + +### `reg.add(…)` — temporary mutable borrow + +Each call to `reg.add(…)` mutably borrows `reg` for the duration of that call. +When `add` returns, the mutable borrow ends and `reg` is available again for +the next call. + +### String literals as `&str` + +`"Henry Osei"` is a string literal — its type is `&str`. It is a reference +pointing into static memory. `add` accepts `name: &str`, so this matches +directly. No allocation happens. Inside `add`, `name.to_string()` is where +the heap allocation occurs. + +### `reg.list_all()` — temporary immutable borrow + +`list_all` takes `&self` — an immutable borrow of `reg`. After it returns, +`reg` is still owned by `main`. At the closing `}` of `main`, `reg` goes out +of scope and is dropped — which drops the `Vec`, which drops every `Student`, +which drops every `String name` on the heap. + +--- + +## Summary map + +``` +main.rs +│ +│ let mut reg = Registry::new(); +│ └── main owns reg (and its Vec) +│ +│ reg.add("Henry", 20, Grade::First, 78.5) +│ ├── &mut self — mutable borrow of reg (temporary) +│ ├── name: &str — immutable borrow of "Henry" (caller keeps it) +│ ├── grade: Grade — ownership of Grade::First moves into Student +│ └── push(student) — Vec takes ownership of Student +│ +│ reg.list_all() +│ ├── &self — immutable borrow of reg (temporary) +│ ├── &self.students — immutable borrow of Vec +│ └── &student — immutable borrow of each Student in the loop +│ └── student.grade.as_str() +│ └── &self on Grade — chained immutable borrow +│ +└── } ← reg dropped → Vec dropped → all Students dropped → all Strings freed +``` + +--- + +## The one pattern to remember + +``` +Does the function need to change the value? + YES → &mut self (mutable borrow) + NO → &self (immutable borrow) + +Does ownership need to leave the caller permanently? + YES → pass by value (e.g. grade: Grade into add()) + NO → pass by reference (e.g. name: &str into add()) +``` + +Rust enforces these choices at compile time. If you get them wrong, the +compiler tells you exactly which rule was broken and on which line — that is +the compiler errors you saw in the previous session. diff --git a/rust_session/assignments/student_registry_v1/notes/referencing_deep_dive.md b/rust_session/assignments/student_registry_v1/notes/referencing_deep_dive.md new file mode 100644 index 00000000..c422de29 --- /dev/null +++ b/rust_session/assignments/student_registry_v1/notes/referencing_deep_dive.md @@ -0,0 +1,209 @@ +# Referencing in Rust — ELI5 + +A reference is just an address — it tells Rust _where_ a value lives in memory, +without taking the value away from its owner. + +If ownership is _having_ something, a reference is _knowing where it is_. + +--- + +## The core idea + +```rust +fn main() { + let score: i32 = 95; + + let r = &score; // r is a reference — it holds the address of `score` + + println!("score = {}", score); // use the original + println!("via r = {}", r); // use the reference — same value + println!("addr = {:p}", r); // {:p} prints the memory address e.g. 0x7ffee3b2c490 +} +``` + +`score` is the value sitting in memory. +`r` is a small variable that holds the _address_ of `score` — nothing more. +Both `score` and `r` print `95` because `r` points straight at `score`. + +--- + +## Dereferencing — following the address + +The `*` operator means "go to the address this reference holds and give me +what is there". + +```rust +fn main() { + let x = 10; + let r = &x; // r holds the address of x + + println!("{}", r); // Rust auto-derefs for println — prints 10 + println!("{}", *r); // explicit deref — also prints 10 + + // Think of it like: + // r = the street address of a house + // *r = walking to that address and looking inside the house +} +``` + +--- + +## Changing a value through a mutable reference + +```rust +fn main() { + let mut points = 50; + let r = &mut points; // mutable reference — can read AND write + + *r = 100; // go to the address and write 100 there + + println!("{}", points); // 100 — the original changed +} +``` + +`r` is not a copy of `points`. It is a direct window into the same memory slot. +Writing through `*r` changes `points` itself. + +--- + +## Printing the address with `{:p}` + +```rust +fn main() { + let name = String::from("Kofi"); + let r = &name; + + println!("value : {}", r); // Kofi + println!("address : {:p}", r); // e.g. 0x7ffee3b2c490 + + // The address will be different every time you run the program. + // The OS places things at different spots in RAM on each run. +} +``` + +`{:p}` is the "pointer" format specifier — it shows the raw memory address +the reference holds. + +--- + +## Multiple references to the same value + +```rust +fn main() { + let city = String::from("Accra"); + + let r1 = &city; + let r2 = &city; + let r3 = &city; + + // All three references point to the SAME memory — no copies made + println!("{:p}", r1); // same address + println!("{:p}", r2); // same address + println!("{:p}", r3); // same address + + println!("{} {} {}", r1, r2, r3); // Accra Accra Accra +} +``` + +References are cheap — they are just addresses (8 bytes on a 64-bit system). +No matter how large the original value is, the reference is always the same tiny size. + +--- + +## References vs owned values — size comparison + +```rust +use std::mem::size_of; +use std::mem::size_of_val; + +fn main() { + let name = String::from("Henry Osei"); // String on heap + let r = &name; + + // String = 3 words on the stack (ptr + len + cap) + println!("size of String : {} bytes", size_of_val(&name)); // 24 bytes + // Reference = 1 pointer + println!("size of &String : {} bytes", size_of::<&String>()); // 8 bytes + + // The reference is always 8 bytes regardless of how long the name is. +} +``` + +--- + +## References into a Vec — what the for loop actually does + +```rust +fn main() { + let students = vec![ + String::from("Henry"), + String::from("Kofi"), + String::from("Esi"), + ]; + + // &students.students borrows the Vec — gives references to each element + for name in &students { + // name is &String — a reference, not an owned String + println!("{:p} → {}", name, name); // address → value + } + + // students is still owned here — the loop only borrowed + println!("total: {}", students.len()); +} +``` + +Without the `&`, the loop would _move_ each `String` out of the `Vec` and the +`Vec` would be unusable afterwards. + +--- + +## In the student registry + +```rust +// list_all iterates with &self.students +// — each `student` is a &Student reference, not an owned Student + +pub fn list_all(&self) { + for student in &self.students { + // student : &Student + // student.name : &String (accessed through the reference) + // student.grade : &Grade (accessed through the reference) + + println!( + "{} {} {}", + student.name, // Rust auto-derefs &Student to reach .name + student.grade.as_str(), // chained reference: &Student → &Grade → &str + student.score, + ); + } + // Nothing was moved. Every Student is still inside the Vec. +} +``` + +--- + +## The difference between `&` and `&mut` in one picture + +``` +Memory slot: [ 95 ] ← the value lives here at address 0xABC + +&score → read-only window into 0xABC + many allowed at the same time + +&mut score → read-write window into 0xABC + only ONE allowed, and no &score at the same time +``` + +--- + +## One-line mental model + +``` +&T → "I know where it lives — I can look" +&mut T → "I know where it lives — I can look and change" +*r → "go to the address r holds and get what's there" +{:p} → "show me the raw address as a number" +``` + +Rust guarantees at compile time that every reference always points to valid +memory — no dangling pointers, no null, no use-after-free. Ever. diff --git a/rust_session/assignments/student_registry_v1/notes/struct.md b/rust_session/assignments/student_registry_v1/notes/struct.md new file mode 100644 index 00000000..162fa495 --- /dev/null +++ b/rust_session/assignments/student_registry_v1/notes/struct.md @@ -0,0 +1,4 @@ +Grouping related data +A struct bundles related fields under one name — like a row in a database table, but typed. Each field has an explicit type: no surprises. + +name: `String` lives on the heap. id, age, score live on the stack. \ No newline at end of file diff --git a/rust_session/assignments/student_registry_v1/src/grade.rs b/rust_session/assignments/student_registry_v1/src/grade.rs new file mode 100644 index 00000000..6e80788c --- /dev/null +++ b/rust_session/assignments/student_registry_v1/src/grade.rs @@ -0,0 +1,35 @@ +#[derive(Debug, PartialEq, Clone)] +pub enum Grade { + First, + Second, + Third, + None +} + +impl Grade { + pub fn as_str(&self) -> &str { + match self { + Grade::First => "Cohort 1", + Grade::Second => "Cohort 2", + Grade::Third => "Cohort 3", + Grade::None => "None" + } + } +} + +#[derive(Debug, Clone)] +pub enum Sex { + Male, + Female, + None +} + +impl Sex { + pub fn to_str(&self) { + match self { + Sex::Male => println!("male: 👨🏾"), + Sex::Female => println!("female: 👧🏾"), + Sex::None => println!("None") + } + } +} diff --git a/rust_session/assignments/student_registry_v1/src/main.rs b/rust_session/assignments/student_registry_v1/src/main.rs new file mode 100644 index 00000000..793429f0 --- /dev/null +++ b/rust_session/assignments/student_registry_v1/src/main.rs @@ -0,0 +1,138 @@ +use std::io; + +mod grade; +mod registry; +mod student_struct; +mod utils; + +use grade::{Grade, Sex}; +use registry::Registry; +use student_struct::Student; + +fn main() { + let mut registry = Registry::new(vec![], 1); + + + println!(""); + println!(""); + println!("=========================="); + println!("STUDENT REGISTRY"); + println!("=========================="); + + loop { + println!(""); + + println!(" Choose An Option"); + println!(""); + println!("1. Add Student"); + println!("2. Get A Student"); + println!("3. All Students"); + println!("4. Update Student Name"); + println!("5. Update Student Age"); + println!("6. Update Student Grade"); + println!("7. Update Student Score"); + println!("8. Delete Student"); + + println!(""); + + let mut choice_input = String::new(); + io::stdin().read_line(&mut choice_input).expect("Failed to read line"); + let choice_input = choice_input.trim(); + println!(""); + println!(""); + + match choice_input { + "1" => { + let name = get_string_input("Fill In *Name* Input"); + let grade = get_string_input("Fill In *Grade* Input"); + let sex = get_string_input("Fill In *Sex* Input"); + let age = get_num_input("Fill In *Age* Input"); + let score = get_fp_input("Fill In *Score* Input"); + registry.add(&name, &sex, &grade, score, age); + } + "2" => { + let id = get_num_input("Fill In *ID* Input"); + registry.get_student(id); + } + "3" => { + registry.list_all(); + } + "4" => { + let new_name = get_string_input("Fill In *Name* Input"); + let id = get_num_input("Fill In *ID* Input"); + + registry.update_name(id, &new_name); + } + "5" => { + let id = get_num_input("Fill In *ID* Input"); + let new_age = get_num_input("Fill In *Age* Input"); + registry.update_age(id, new_age); + } + "6" => { + let grade = get_string_input("Fill In *Grade* Input"); + let id = get_num_input("Fill In *ID* Input"); + + registry.update_grade(id, &grade); + } + "7" => { + let new_score = get_fp_input("Fill In *Grade* Input"); + let id = get_num_input("Fill In *ID* Input"); + + registry.update_score(id, new_score); + } + "8" => { + let id = get_num_input("Fill In *ID* Input"); + registry.delete_student(id); + } + _ => { + println!("/////////"); + println!("Invalid Input"); + println!("/////////"); + } + } + + + } + + + + + + + + + + // let s: Student = Student::new(1, String::from("Testimony"), 16, Sex::Female, Grade::Third, 40.5); + // let b: Student = Student::new(2, String::from("Henry"), 20, Sex::Male, Grade::Third, 70.5); + + // let student_vec = vec![s, b]; + + // let registry = Registry::new(student_vec, 1); + + // registry.get_student(1); + // // println!("student1 is: {:?}", student1); + +} + +fn get_string_input(prompt: &str) -> String { + println!("{}", prompt); + let mut input = String::new(); + io::stdin().read_line(&mut input).expect("Failed to read line"); + input.trim().to_string() +} + +fn get_num_input(prompt: &str) -> u32 { + println!("{}", prompt); + let mut num_input = String::new(); + io::stdin().read_line(&mut num_input).expect("Feailed to read line"); + let num_input: u32 = num_input.trim().parse().expect("Failed to parse"); + num_input +} + +fn get_fp_input(prompt: &str) -> f32 { + println!("{}", prompt); + let mut num_input = String::new(); + io::stdin().read_line(&mut num_input).expect("Feailed to read line"); + let num_input: f32 = num_input.trim().parse().expect("Failed to parse"); + num_input +} \ No newline at end of file diff --git a/rust_session/assignments/student_registry_v1/src/registry.rs b/rust_session/assignments/student_registry_v1/src/registry.rs new file mode 100644 index 00000000..fa14c763 --- /dev/null +++ b/rust_session/assignments/student_registry_v1/src/registry.rs @@ -0,0 +1,142 @@ +use crate::grade::{Grade, Sex}; +use crate::student_struct::Student; + +#[derive(Clone)] +pub struct Registry { + pub students: Vec, + next_id: u32, +} + +impl Registry { + pub fn new(student: Vec, id: u32) -> Registry { + Registry { + students: student, + next_id: id + } + } + + pub fn add(&mut self, name: &str, sex: &str, grade: &str, score: f32, age: u32) { + let id = self.next_id; + + let sex_x = if sex.to_lowercase().contains("male"){ + Sex::Male + }else if sex.to_lowercase().contains("female") { + Sex::Female + }else { + Sex::None + }; + + let gradex = if grade.to_lowercase().contains("first"){ + Grade::First + }else if grade.to_lowercase().contains("second") { + Grade::Second + }else if grade.to_lowercase().contains("third") { + Grade::Third + }else { + Grade::None + }; + + let student = Student::new(id, name.to_string(), age, sex_x, gradex, score); + println!("Added: {} (ID {})", student.name, student.id); + self.students.push(student); + self.next_id += 1; + } + + pub fn list_all(&self) { + if self.students.is_empty() { + println!(" (no students enrolled yet)"); + return; + } + println!( + " {:>5} {:<20} {:<6} {:<10} {}", + "ID", "Name", "Age", "Grade", "Score" + ); + println!(" {}", "-".repeat(55)); + for student in &self.students { + println!( + " {:>5} {:<20} {:>6} {:<10} {:.1}", + student.id, + student.name, + student.age, + student.grade.as_str(), + student.score, + ); + } + } + + pub fn get_student(&self, id: u32) { + if self.students.is_empty(){ + println!("Invalid ID"); + return; + } + + for i in &self.students{ + if i.id == id { + println!(" + Student id: {}, + Student name: {}, + Student age: {}, + Student grade: {:?}, + Student score: {}, + ", i.id, i.name, i.age, i.grade, i.score) + }else { + println!("Invalid ID") + } + } + } + + pub fn update_name(&mut self, id: u32, new_name: &str) { + if let Some(index) = self.students.iter().position(|student| student.id == id) { + let student = &mut self.students[index]; + if student.name != new_name { + student.name = new_name.to_string(); + return; + } + } + } + + pub fn update_age(&mut self, id: u32, new_age: u32) { + if let Some(index) = self.students.iter().position(|student| student.id == id) { + let student = &mut self.students[index]; + + if student.age != new_age { + student.age = new_age; + return; + } + } + } + + pub fn update_score(&mut self, id: u32, new_score: f32) { + if let Some(index) = self.students.iter().position(|student| student.id == id) { + let student = &mut self.students[index]; + + if student.score != new_score { + student.score = new_score; + return; + } + } + } + + pub fn update_grade(&mut self, id: u32, input: &str) { + if let Some(index) = self.students.iter().position(|student| student.id == id ){ + let student = &mut self.students[index]; + if input.to_lowercase().contains("first"){ + student.grade = Grade::First + } + if input.to_lowercase().contains("second"){ + student.grade = Grade::Second + } + if input.to_lowercase().contains("third"){ + student.grade = Grade::Third + } + } + } + + pub fn delete_student(&mut self, id: u32) { + if let Some(index) = self.students.iter().position(|student| student.id == id) { + self.students.remove(index); + }else { + + } + } +} diff --git a/rust_session/assignments/student_registry_v1/src/registry_struct.rs b/rust_session/assignments/student_registry_v1/src/registry_struct.rs new file mode 100644 index 00000000..8b076a03 --- /dev/null +++ b/rust_session/assignments/student_registry_v1/src/registry_struct.rs @@ -0,0 +1,6 @@ +use crate::student_struct; + +pub struct Registry { + students: Vec, // Vec = "a list of Student values" + next_id: u32, // auto-increment counter for IDs +} \ No newline at end of file diff --git a/rust_session/assignments/student_registry_v1/src/student_struct.rs b/rust_session/assignments/student_registry_v1/src/student_struct.rs new file mode 100644 index 00000000..f349215b --- /dev/null +++ b/rust_session/assignments/student_registry_v1/src/student_struct.rs @@ -0,0 +1,42 @@ +use crate::grade::{Grade, Sex}; + +// A struct groups related pieces of data under one name. +// Think of it as a custom data type you design yourself. + +#[derive(Debug, Clone)] +pub struct Student { + pub id: u32, // u32 = unsigned 32-bit integer (no negatives) + pub name: String, // String = heap-allocated, growable text + pub age: u32, + pub sex: Sex, + pub grade: Grade, // our own enum type from above + pub score: f32, +} + +// This is the implementation of the student struct with its corresponding methods +impl Student { + pub fn new(id: u32, name: String, age: u32, sex: Sex, grade: Grade, score: f32) -> Student { + Student { + id, + name, + age, + sex, + grade, + score, + } + } +} + +#[derive(Debug)] +pub enum Status { + Pending, + Ongoing, + Completed, +} + +pub struct Todo { + id: u32, + title: String, + description: String, + status: Status, +} diff --git a/rust_session/assignments/student_registry_v1/src/utils.rs b/rust_session/assignments/student_registry_v1/src/utils.rs new file mode 100644 index 00000000..ab2c8eeb --- /dev/null +++ b/rust_session/assignments/student_registry_v1/src/utils.rs @@ -0,0 +1,3 @@ +// pub fn to_str(x: String) -> &'static str { +// x.as_str() +// } diff --git a/rust_session/assignments/student_registry_v2/.gitignore b/rust_session/assignments/student_registry_v2/.gitignore new file mode 100644 index 00000000..bb68a347 --- /dev/null +++ b/rust_session/assignments/student_registry_v2/.gitignore @@ -0,0 +1 @@ +./target \ No newline at end of file diff --git a/rust_session/assignments/student_registry_v2/Cargo.lock b/rust_session/assignments/student_registry_v2/Cargo.lock new file mode 100644 index 00000000..365eeef6 --- /dev/null +++ b/rust_session/assignments/student_registry_v2/Cargo.lock @@ -0,0 +1,525 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "bitflags" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "colored" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2025f20d7a4fa7785846e7b63d10a76d3f1cee98ee5cb79ea59703f95e42162" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "log" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5" + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "student_registry" +version = "0.1.0" +dependencies = [ + "colored", + "uuid", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "uuid" +version = "1.23.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "144d6b123cef80b301b8f72a9e2ca4370ddec21950d0a103dd22c437006d2db7" +dependencies = [ + "getrandom", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasip2" +version = "1.0.3+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +dependencies = [ + "wit-bindgen 0.57.1", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen 0.51.0", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a254a4b10c19a76f09a27640e7ffbf9bc30bf67e16a3bf28aaefa4920fe81563" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24a40fc75b0ec6f3746ceb10d36f53a93dcd68a93b11b6445983945d79eba0dc" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "908f34bd9b9ce3d4caf07b72dfab63d61504d156856c6bd3cd87fa350cf3985b" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7acbf7616c27b194bbb550bf77ed0c2c3e5b7fd1260a93082b95fb7f47959b92" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/rust_session/assignments/student_registry_v2/Cargo.toml b/rust_session/assignments/student_registry_v2/Cargo.toml new file mode 100644 index 00000000..bd2c18f7 --- /dev/null +++ b/rust_session/assignments/student_registry_v2/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "student_registry" +version = "0.1.0" +edition = "2021" + +[dependencies] +colored = "3.1.1" +uuid = { version="1", features = ["v4"] } diff --git a/rust_session/assignments/student_registry_v2/README.md b/rust_session/assignments/student_registry_v2/README.md new file mode 100644 index 00000000..9441b751 --- /dev/null +++ b/rust_session/assignments/student_registry_v2/README.md @@ -0,0 +1,104 @@ +# Rust Student Registry +### A beginner project covering: `struct` · `Vec` · `enum` · `Option` · `impl` + +```bash +// ============================================================ +// STUDENT REGISTRY — Beginner Rust Project +// Concepts covered: +// • struct — grouping related data +// • enum — a value that can be one of several variants +// • Vec — a growable list +// • Option — a value that may or may not exist +// • impl block — adding methods to a struct +// • pattern matching (match, if let) +// • ownership & borrowing in practice +// ============================================================ +``` + +--- + +## How to run + +### Step 1 — Install Rust (if you haven't yet) +```bash +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +``` +Then restart your terminal, or run: +```bash +source "$HOME/.cargo/env" +``` + +### Step 2 — Verify the install +```bash +rustc --version # e.g. rustc 1.78.0 +cargo --version # e.g. cargo 1.78.0 +``` + +### Step 3 — Run the project +```bash +# Clone / copy this folder, then: +cd student_registry + +cargo run # compiles + runs in one command +``` + +### Other useful commands +```bash +cargo build # compile only (output goes to ./target/debug/) +cargo check # fast type-check without producing a binary (great while learning) +cargo run --release # compile with full optimisations (faster binary) +``` + +--- + +## What the project teaches + +| Concept | Where to look in main.rs | +|---|---| +| `struct` | `Student` and `Registry` definitions (~line 30, 90) | +| `enum` | `Grade` definition (~line 14) | +| `impl` block | `impl Grade`, `impl Student`, `impl Registry` | +| `Vec` | `Registry.students` field; `push`, `retain`, `iter` | +| `Option` | `find_by_name`, `find_by_id` return types; `demo_option()` | +| `match` | `Grade::as_str`, `letter_grade`, search demos | +| `if let` | `update_score`, `summary`, `demo_option` | +| Ownership / borrowing | `&self` vs `&mut self` on every method | +| Iterator methods | `summary()` — `.map()`, `.sum()`, `.max_by()` | + +--- + +## Expected output (abridged) + +``` + ╔══════════════════════════════════════╗ + ║ RUST STUDENT REGISTRY v1.0 ║ + ║ struct · Vec · enum · Option · impl ║ + ╚══════════════════════════════════════╝ + + ┌─ 1. Adding students (struct + Vec::push) + ✅ Added: Kay (ID 1) + ✅ Added: Yusrah (ID 2) + ... + + ┌─ 3. Search by name (Option<&Student>) + Found → ID: 2 Grade: 2nd Year Score: 64 + 'Yaw Owusu' not found in registry. + + ┌─ 8. Summary (iterators: map, sum, max_by) + Total students : 4 + Average score : 71.5 + Top student : Esi Boateng (91.0) +``` + +--- + +## Project structure + +``` +student_registry/ +├── Cargo.toml ← project metadata & dependencies +└── src/ + └── main.rs ← all code lives here (heavily annotated) +``` + +Everything is in one file intentionally — beginners don't need to navigate modules yet. diff --git a/rust_session/assignments/student_registry_v2/notes/borrowing_deep_dive.md b/rust_session/assignments/student_registry_v2/notes/borrowing_deep_dive.md new file mode 100644 index 00000000..76411372 --- /dev/null +++ b/rust_session/assignments/student_registry_v2/notes/borrowing_deep_dive.md @@ -0,0 +1,167 @@ +# Borrowing Deep Dive +Borrowing is like lending something to a friend. + +--- + +## The core idea + +```rust +fn main() { + let book = String::from("Rust Programming"); + + read(&book); // lend the book to `read` + + // book is still yours after the function returns + println!("I still have: {}", book); +} + +fn read(b: &String) { // b is a borrowed reference — not the owner + println!("Reading: {}", b); +} // borrow ends here — nothing is dropped +``` + +You still own `book`. `read` just borrowed it temporarily. When `read` finishes, +the book comes back to you automatically. + +--- + +## Without borrowing — ownership moves away + +```rust +fn main() { + let book = String::from("Rust Programming"); + + read(book); // ownership MOVES into read — you no longer have it + + println!("{}", book); // ❌ compile error: book was moved +} + +fn read(b: String) { // b owns the book now + println!("Reading: {}", b); +} // b is dropped here — book is gone forever +``` + +This is why borrowing exists. Without `&`, the value moves in and never comes back. + +--- + +## Two kinds of borrow + +### 1. Immutable borrow `&T` — look but don't touch + +```rust +fn main() { + let score = 95; + + print_score(&score); // lend score for reading + + println!("score is still {}", score); // ✅ still usable +} + +fn print_score(s: &i32) { + println!("Score: {}", s); + // *s = 100; // ❌ can't change it — it's an immutable borrow +} +``` + +### 2. Mutable borrow `&mut T` — borrow AND change it + +```rust +fn main() { + let mut score = 95; // must be `mut` to lend mutably + + add_bonus(&mut score); // lend score for writing + + println!("new score: {}", score); // 100 +} + +fn add_bonus(s: &mut i32) { + *s += 5; // * means "go to what this points to and change it" +} +``` + +--- + +## The two rules Rust enforces + +### Rule 1 — as many readers as you like + +```rust +fn main() { + let name = String::from("Henry"); + + let r1 = &name; // borrow 1 — fine + let r2 = &name; // borrow 2 — also fine + let r3 = &name; // borrow 3 — still fine + + println!("{} {} {}", r1, r2, r3); // ✅ multiple readers are safe +} +``` + +Think of a book in a library — unlimited people can read the same book at the +same time because no one is changing it. + +### Rule 2 — only ONE writer, and no readers at the same time + +```rust +fn main() { + let mut name = String::from("Henry"); + + let r1 = &name; // immutable borrow + let r2 = &mut name; // ❌ compile error — can't have &mut while &name exists + + println!("{} {}", r1, r2); +} +``` + +```rust +fn main() { + let mut name = String::from("Henry"); + + let r1 = &name; + println!("{}", r1); // r1 last used here — borrow ends + + let r2 = &mut name; // ✅ fine now — r1 is done + r2.push_str(" Osei"); + println!("{}", r2); // "Henry Osei" +} +``` + +Think of a whiteboard — unlimited people can read it, but the moment someone +starts writing on it, everyone else has to step back. + +--- + +## In the student registry + +```rust +// &mut self — we need to CHANGE the registry (push a new student) +pub fn add(&mut self, name: &str, ...) { + // ^^^ + // mutable borrow — we will modify self.students + + self.students.push(student); // this is the change +} + +// &self — we only READ the registry (print students) +pub fn list_all(&self) { + // ^^^^^ + // immutable borrow — we never change anything + + for student in &self.students { // borrow each student for printing + println!("{}", student.name); + } +} +``` + +--- + +## One-line mental model + +``` +&T → "I want to look at it" +&mut T → "I want to look at it AND change it" +T → "I want to own it and take it with me" +``` + +Rust checks these rules at compile time — zero runtime cost, zero crashes. diff --git a/rust_session/assignments/student_registry_v2/notes/core_logic.md b/rust_session/assignments/student_registry_v2/notes/core_logic.md new file mode 100644 index 00000000..5383ef4e --- /dev/null +++ b/rust_session/assignments/student_registry_v2/notes/core_logic.md @@ -0,0 +1,315 @@ +# Ownership, Borrowing & Referencing in the Student Registry + +This document walks through every place ownership, borrowing, and referencing +appear in the four files of the student registry project — +`grade.rs`, `student_struct.rs`, `registry.rs`, and `main.rs` — +and explains _why_ each one is used. + +--- + +## Quick reminder: the three concepts + +| Concept | Syntax | Meaning | +| -------------------------------- | --------------- | ------------------------------------------------------------------------------------------- | +| **Ownership** | `let x = value` | One variable is responsible for the value. When it goes out of scope, the value is dropped. | +| **Mutable borrow** | `&mut T` | Temporarily lend the value for reading AND writing. No ownership transfer. | +| **Immutable borrow / reference** | `&T` | Temporarily lend the value for reading only. No ownership transfer. | + +--- + +## 1. `grade.rs` + +```rust +pub fn as_str(&self) -> &str { + match self { + Grade::First => "1st Year", + Grade::Second => "2nd Year", + Grade::Third => "3rd Year", + } +} +``` + +### `&self` — immutable borrow of Grade + +`as_str` only needs to _read_ the variant — it never changes it. +`&self` is an immutable reference to the `Grade` value. + +- The caller keeps ownership of the `Grade`. +- `as_str` borrows it just long enough to run the `match` and return a string. +- When `as_str` returns, the borrow ends. The caller still owns the `Grade`. + +### `-> &str` — returning a reference to static memory + +The returned `&str` points to string literals like `"1st Year"` which live in +the program's static memory (baked into the binary). No heap allocation occurs. +The reference is valid for the entire lifetime of the program. + +--- + +## 2. `student_struct.rs` + +```rust +use crate::grade::Grade; + +pub struct Student { + pub id: u32, + pub name: String, + pub age: u8, + pub grade: Grade, + pub score: f32, +} + +impl Student { + pub fn new(id: u32, name: String, age: u8, grade: Grade, score: f32) -> Student { + Student { id, name, age, grade, score } + } +} +``` + +### `fn new(…) -> Student` — ownership transfer via return value + +`new` takes all five arguments **by value** — it takes ownership of each one: + +| Parameter | Type | What happens | +| --------- | -------- | ------------------------------------------------------ | +| `id` | `u32` | Copied (primitive, `Copy` trait) | +| `name` | `String` | **Moved** into the struct — heap memory is transferred | +| `age` | `u8` | Copied (primitive) | +| `grade` | `Grade` | **Moved** into the struct — enum value transferred | +| `score` | `f32` | Copied (primitive) | + +When `new` returns the `Student`, ownership of the entire struct — including +the `String` on the heap — moves to whoever called `new`. + +### Why `String` and not `&str`? + +`String` is an _owned_, heap-allocated string. The struct needs to _own_ its +`name` field so the name lives as long as the `Student` does. If we used `&str` +(a borrowed reference), we would have to track where the original string lives +and guarantee it outlives the struct — that is lifetime annotation territory, +which is more advanced. + +--- + +## 3. `registry.rs` + +```rust +pub struct Registry { + pub students: Vec, + next_id: u32, +} +``` + +### `Vec` — Registry owns all students + +`Registry` owns the `Vec`, and the `Vec` owns every `Student` inside it. +This is a chain of ownership: + +``` +Registry + └── Vec (heap) + ├── Student { id, name, age, grade, score } + ├── Student { … } + └── Student { … } +``` + +When `Registry` is dropped, the `Vec` is dropped, which drops every `Student`, +which drops every `String` inside each student. One owner, one cleanup — no +manual `free()` needed. + +--- + +### `pub fn new() -> Registry` + +```rust +pub fn new() -> Registry { + Registry { + students: Vec::new(), + next_id: 1, + } +} +``` + +Returns an owned `Registry` by value. The caller takes ownership. +`Vec::new()` creates an empty `Vec` — no heap allocation yet. +The heap only gets used when the first `push()` happens. + +--- + +### `pub fn add(&mut self, name: &str, age: u8, grade: Grade, score: f32)` + +```rust +pub fn add(&mut self, name: &str, age: u8, grade: Grade, score: f32) { + let id = self.next_id; + let student = Student::new(id, name.to_string(), age, grade, score); + println!(" ✅ Added: {} (ID {})", student.name, student.id); + self.students.push(student); + self.next_id += 1; +} +``` + +There are three distinct ownership/borrowing events here: + +#### `&mut self` — mutable borrow of Registry + +`add` needs to _change_ the registry — it pushes a new student and increments +`next_id`. So it takes a **mutable borrow** of the whole `Registry`. + +- The caller keeps ownership of the `Registry`. +- `add` can read and write any field on it. +- Only one `&mut` borrow can exist at a time — Rust enforces this. + +#### `name: &str` — immutable reference to a string + +`name` comes in as `&str` — a **borrowed reference** to a string slice. +`add` does not take ownership of the string. It only needs to read the +characters long enough to call `name.to_string()`. + +`name.to_string()` creates a brand-new owned `String` on the heap — that +owned copy is what gets moved into `Student::new`. The original string the +caller passed in is untouched and still owned by the caller. + +#### `grade: Grade` — ownership moves in + +`grade` is passed **by value** — ownership moves from the caller into `add`, +then immediately into `Student::new`, then into the `Student` struct. +After `reg.add(…, Grade::First, …)` in `main.rs`, the `Grade::First` value +lives inside the `Student` inside the `Vec`. The caller no longer holds it. + +#### `self.students.push(student)` — Vec takes ownership of Student + +After `println!`, `student` is moved into the `Vec` via `push`. From this +point: + +- `student` is no longer accessible as a local variable. +- The `Vec` is the owner. +- The memory for the student (including its `String name` on the heap) will be + freed only when the `Vec` itself is dropped. + +--- + +### `pub fn list_all(&self)` + +```rust +pub fn list_all(&self) { + for student in &self.students { + println!( + " {:>5} {:<20} {:>6} {:<10} {:.1}", + student.id, + student.name, + student.age, + student.grade.as_str(), + student.score, + ); + } +} +``` + +#### `&self` — immutable borrow of Registry + +`list_all` only reads — it never changes anything. `&self` is the correct +choice. The caller keeps ownership of the `Registry` and can use it again +after `list_all` returns. + +#### `for student in &self.students` — borrowing each element + +The `&` before `self.students` means we are iterating over **references** to +each `Student` — not moving them out of the `Vec`. + +- `student` inside the loop is `&Student` — a borrowed reference. +- The `Vec` still owns every student. +- Reading `student.id`, `student.name`, `student.age`, `student.score` is + fine because we are borrowing those fields through the reference. + +#### `student.grade.as_str()` — chained borrow + +`student` is `&Student`, so `student.grade` is accessing the `Grade` field +through a reference. Rust auto-derefs this for us. +`as_str` then takes `&self` on the `Grade` — another immutable borrow layered +on top. Both borrows exist only for the duration of the `println!` line, then +they end. + +--- + +## 4. `main.rs` + +```rust +fn main() { + let mut reg = Registry::new(); + + reg.add("Henry Osei", 20, Grade::First, 78.5); + reg.add("Kofi Mensah", 22, Grade::Second, 64.0); + reg.add("Esi Boateng", 21, Grade::First, 91.0); + + reg.list_all(); +} +``` + +### `let mut reg` — main owns Registry + +`main` owns `reg`. It must be `mut` because `add` takes `&mut self` — you +cannot mutably borrow something that was not declared mutable. + +### `reg.add(…)` — temporary mutable borrow + +Each call to `reg.add(…)` mutably borrows `reg` for the duration of that call. +When `add` returns, the mutable borrow ends and `reg` is available again for +the next call. + +### String literals as `&str` + +`"Henry Osei"` is a string literal — its type is `&str`. It is a reference +pointing into static memory. `add` accepts `name: &str`, so this matches +directly. No allocation happens. Inside `add`, `name.to_string()` is where +the heap allocation occurs. + +### `reg.list_all()` — temporary immutable borrow + +`list_all` takes `&self` — an immutable borrow of `reg`. After it returns, +`reg` is still owned by `main`. At the closing `}` of `main`, `reg` goes out +of scope and is dropped — which drops the `Vec`, which drops every `Student`, +which drops every `String name` on the heap. + +--- + +## Summary map + +``` +main.rs +│ +│ let mut reg = Registry::new(); +│ └── main owns reg (and its Vec) +│ +│ reg.add("Henry", 20, Grade::First, 78.5) +│ ├── &mut self — mutable borrow of reg (temporary) +│ ├── name: &str — immutable borrow of "Henry" (caller keeps it) +│ ├── grade: Grade — ownership of Grade::First moves into Student +│ └── push(student) — Vec takes ownership of Student +│ +│ reg.list_all() +│ ├── &self — immutable borrow of reg (temporary) +│ ├── &self.students — immutable borrow of Vec +│ └── &student — immutable borrow of each Student in the loop +│ └── student.grade.as_str() +│ └── &self on Grade — chained immutable borrow +│ +└── } ← reg dropped → Vec dropped → all Students dropped → all Strings freed +``` + +--- + +## The one pattern to remember + +``` +Does the function need to change the value? + YES → &mut self (mutable borrow) + NO → &self (immutable borrow) + +Does ownership need to leave the caller permanently? + YES → pass by value (e.g. grade: Grade into add()) + NO → pass by reference (e.g. name: &str into add()) +``` + +Rust enforces these choices at compile time. If you get them wrong, the +compiler tells you exactly which rule was broken and on which line — that is +the compiler errors you saw in the previous session. diff --git a/rust_session/assignments/student_registry_v2/notes/referencing_deep_dive.md b/rust_session/assignments/student_registry_v2/notes/referencing_deep_dive.md new file mode 100644 index 00000000..c422de29 --- /dev/null +++ b/rust_session/assignments/student_registry_v2/notes/referencing_deep_dive.md @@ -0,0 +1,209 @@ +# Referencing in Rust — ELI5 + +A reference is just an address — it tells Rust _where_ a value lives in memory, +without taking the value away from its owner. + +If ownership is _having_ something, a reference is _knowing where it is_. + +--- + +## The core idea + +```rust +fn main() { + let score: i32 = 95; + + let r = &score; // r is a reference — it holds the address of `score` + + println!("score = {}", score); // use the original + println!("via r = {}", r); // use the reference — same value + println!("addr = {:p}", r); // {:p} prints the memory address e.g. 0x7ffee3b2c490 +} +``` + +`score` is the value sitting in memory. +`r` is a small variable that holds the _address_ of `score` — nothing more. +Both `score` and `r` print `95` because `r` points straight at `score`. + +--- + +## Dereferencing — following the address + +The `*` operator means "go to the address this reference holds and give me +what is there". + +```rust +fn main() { + let x = 10; + let r = &x; // r holds the address of x + + println!("{}", r); // Rust auto-derefs for println — prints 10 + println!("{}", *r); // explicit deref — also prints 10 + + // Think of it like: + // r = the street address of a house + // *r = walking to that address and looking inside the house +} +``` + +--- + +## Changing a value through a mutable reference + +```rust +fn main() { + let mut points = 50; + let r = &mut points; // mutable reference — can read AND write + + *r = 100; // go to the address and write 100 there + + println!("{}", points); // 100 — the original changed +} +``` + +`r` is not a copy of `points`. It is a direct window into the same memory slot. +Writing through `*r` changes `points` itself. + +--- + +## Printing the address with `{:p}` + +```rust +fn main() { + let name = String::from("Kofi"); + let r = &name; + + println!("value : {}", r); // Kofi + println!("address : {:p}", r); // e.g. 0x7ffee3b2c490 + + // The address will be different every time you run the program. + // The OS places things at different spots in RAM on each run. +} +``` + +`{:p}` is the "pointer" format specifier — it shows the raw memory address +the reference holds. + +--- + +## Multiple references to the same value + +```rust +fn main() { + let city = String::from("Accra"); + + let r1 = &city; + let r2 = &city; + let r3 = &city; + + // All three references point to the SAME memory — no copies made + println!("{:p}", r1); // same address + println!("{:p}", r2); // same address + println!("{:p}", r3); // same address + + println!("{} {} {}", r1, r2, r3); // Accra Accra Accra +} +``` + +References are cheap — they are just addresses (8 bytes on a 64-bit system). +No matter how large the original value is, the reference is always the same tiny size. + +--- + +## References vs owned values — size comparison + +```rust +use std::mem::size_of; +use std::mem::size_of_val; + +fn main() { + let name = String::from("Henry Osei"); // String on heap + let r = &name; + + // String = 3 words on the stack (ptr + len + cap) + println!("size of String : {} bytes", size_of_val(&name)); // 24 bytes + // Reference = 1 pointer + println!("size of &String : {} bytes", size_of::<&String>()); // 8 bytes + + // The reference is always 8 bytes regardless of how long the name is. +} +``` + +--- + +## References into a Vec — what the for loop actually does + +```rust +fn main() { + let students = vec![ + String::from("Henry"), + String::from("Kofi"), + String::from("Esi"), + ]; + + // &students.students borrows the Vec — gives references to each element + for name in &students { + // name is &String — a reference, not an owned String + println!("{:p} → {}", name, name); // address → value + } + + // students is still owned here — the loop only borrowed + println!("total: {}", students.len()); +} +``` + +Without the `&`, the loop would _move_ each `String` out of the `Vec` and the +`Vec` would be unusable afterwards. + +--- + +## In the student registry + +```rust +// list_all iterates with &self.students +// — each `student` is a &Student reference, not an owned Student + +pub fn list_all(&self) { + for student in &self.students { + // student : &Student + // student.name : &String (accessed through the reference) + // student.grade : &Grade (accessed through the reference) + + println!( + "{} {} {}", + student.name, // Rust auto-derefs &Student to reach .name + student.grade.as_str(), // chained reference: &Student → &Grade → &str + student.score, + ); + } + // Nothing was moved. Every Student is still inside the Vec. +} +``` + +--- + +## The difference between `&` and `&mut` in one picture + +``` +Memory slot: [ 95 ] ← the value lives here at address 0xABC + +&score → read-only window into 0xABC + many allowed at the same time + +&mut score → read-write window into 0xABC + only ONE allowed, and no &score at the same time +``` + +--- + +## One-line mental model + +``` +&T → "I know where it lives — I can look" +&mut T → "I know where it lives — I can look and change" +*r → "go to the address r holds and get what's there" +{:p} → "show me the raw address as a number" +``` + +Rust guarantees at compile time that every reference always points to valid +memory — no dangling pointers, no null, no use-after-free. Ever. diff --git a/rust_session/assignments/student_registry_v2/notes/struct.md b/rust_session/assignments/student_registry_v2/notes/struct.md new file mode 100644 index 00000000..162fa495 --- /dev/null +++ b/rust_session/assignments/student_registry_v2/notes/struct.md @@ -0,0 +1,4 @@ +Grouping related data +A struct bundles related fields under one name — like a row in a database table, but typed. Each field has an explicit type: no surprises. + +name: `String` lives on the heap. id, age, score live on the stack. \ No newline at end of file diff --git a/rust_session/assignments/student_registry_v2/src/grade.rs b/rust_session/assignments/student_registry_v2/src/grade.rs new file mode 100644 index 00000000..96e8dc78 --- /dev/null +++ b/rust_session/assignments/student_registry_v2/src/grade.rs @@ -0,0 +1,35 @@ +#[derive(Debug, PartialEq)] +pub enum Grade { + First, + Second, + Third, + None +} + +impl Grade { + pub fn as_str(&self) -> &str { + match self { + Grade::First => "Cohort 1", + Grade::Second => "Cohort 2", + Grade::Third => "Cohort 3", + Grade::None => "None" + } + } +} + +#[derive(Debug)] +pub enum Sex { + Male, + Female, + None +} + +impl Sex { + pub fn to_str(&self) { + match self { + Sex::Male => println!("male: 👨🏾"), + Sex::Female => println!("female: 👧🏾"), + Sex::None => println!("None") + } + } +} diff --git a/rust_session/assignments/student_registry_v2/src/main.rs b/rust_session/assignments/student_registry_v2/src/main.rs new file mode 100644 index 00000000..61864f6b --- /dev/null +++ b/rust_session/assignments/student_registry_v2/src/main.rs @@ -0,0 +1,129 @@ +use std::io; +use colored::*; + +mod grade; +mod registry; +mod student_struct; +mod utils; + +use grade::{Grade, Sex}; +use registry::Registry; +use student_struct::Student; +use uuid::Uuid; + +fn main() { + let mut registry = Registry::new(vec![]); + + + println!(""); + println!(""); + println!("{}", "==========================".yellow()); + println!("{}", "STUDENT REGISTRY".yellow().bold()); + println!("{}", "==========================".yellow().bold()); + + loop { + println!(""); + + println!("{}", "____Choose An Option____".bright_green().black()); + println!(""); + println!("{}", "1. Add Student".black().bold()); + println!("{}", "2. Get A Student".black().bold()); + println!("{}", "3. All Students".black().bold()); + println!("{}", "4. Update Student Name".black().bold()); + println!("{}", "5. Update Student Age".black().bold()); + println!("{}", "6. Update Student Grade".black().bold()); + println!("{}", "7. Update Student Score".black().bold()); + println!("{}", "8. Delete Student".black().bold()); + + println!(""); + + let mut choice_input = String::new(); + io::stdin().read_line(&mut choice_input).expect("Failed to read line"); + let choice_input = choice_input.trim(); + println!(""); + println!(""); + + match choice_input { + "1" => { + let name = get_string_input("Fill In *Name* Input"); + let grade = get_string_input("Fill In *Grade* Input"); + let sex = get_string_input("Fill In *Sex* Input"); + let age = get_num_input("Fill In *Age* Input"); + let score = get_fp_input("Fill In *Score* Input"); + registry.add(&name, &sex, &grade, score, age); + } + "2" => { + let id = get_uuid_input("Fill In *ID* Input"); + registry.get_student(id); + } + "3" => { + registry.list_all(); + } + "4" => { + let new_name = get_string_input("Fill In *Name* Input"); + let id = get_uuid_input("Fill In *ID* Input"); + + registry.update_name(id, &new_name); + } + "5" => { + let id = get_uuid_input("Fill In *ID* Input"); + let new_age = get_num_input("Fill In *Age* Input"); + registry.update_age(id, new_age); + } + "6" => { + let grade = get_string_input("Fill In *Grade* Input"); + let id = get_uuid_input("Fill In *ID* Input"); + + registry.update_grade(id, &grade); + } + "7" => { + let new_score = get_fp_input("Fill In *Grade* Input"); + let id = get_uuid_input("Fill In *ID* Input"); + + registry.update_score(id, new_score); + } + "8" => { + let id = get_uuid_input("Fill In *ID* Input"); + registry.delete_student(id); + } + _ => { + println!("/////////"); + println!("Invalid Input"); + println!("/////////"); + } + } + + + } +} + +fn get_string_input(prompt: &str) -> String { + println!("{}", prompt); + let mut input = String::new(); + io::stdin().read_line(&mut input).expect("Failed to read line"); + input.trim().to_string() +} + +fn get_num_input(prompt: &str) -> u32 { + println!("{}", prompt); + let mut num_input = String::new(); + io::stdin().read_line(&mut num_input).expect("Feailed to read line"); + let num_input: u32 = num_input.trim().parse().expect("Failed to parse"); + num_input +} + +fn get_uuid_input(prompt: &str) -> Uuid { + println!("{}", prompt); + let mut num_input = String::new(); + io::stdin().read_line(&mut num_input).expect("Feailed to read line"); + let num_input: Uuid = num_input.trim().parse().expect("Failed to parse"); + num_input +} + +fn get_fp_input(prompt: &str) -> f32 { + println!("{}", prompt); + let mut num_input = String::new(); + io::stdin().read_line(&mut num_input).expect("Feailed to read line"); + let num_input: f32 = num_input.trim().parse().expect("Failed to parse"); + num_input +} \ No newline at end of file diff --git a/rust_session/assignments/student_registry_v2/src/registry.rs b/rust_session/assignments/student_registry_v2/src/registry.rs new file mode 100644 index 00000000..3a9cfba3 --- /dev/null +++ b/rust_session/assignments/student_registry_v2/src/registry.rs @@ -0,0 +1,127 @@ +use uuid::Uuid; + +use crate::grade::{Grade, Sex}; +use crate::student_struct::Student; + +pub struct Registry { + pub students: Vec, +} + +impl Registry { + pub fn new(student: Vec) -> Registry { + Registry { + students: student, + } + } + + pub fn add(&mut self, name: &str, sex: &str, grade: &str, score: f32, age: u32) { + let id = Uuid::new_v4(); + + let sex_x = if sex.to_lowercase().contains("male"){ + Sex::Male + }else if sex.to_lowercase().contains("female") { + Sex::Female + }else { + Sex::None + }; + + let gradex = if grade.to_lowercase().contains("first"){ + Grade::First + }else if grade.to_lowercase().contains("second") { + Grade::Second + }else if grade.to_lowercase().contains("third") { + Grade::Third + }else { + Grade::None + }; + + let student = Student::new(id, name.to_string(), age, sex_x, gradex, score); + println!("Added: {} (ID {})", student.name, student.id); + self.students.push(student); + } + + pub fn list_all(&self) { + if self.students.is_empty() { + println!(" (no students enrolled yet)"); + return; + } + println!( + " {:>5} {:<20} {:<6} {:<10} {}", + "ID", "Name", "Age", "Grade", "Score" + ); + println!(" {}", "-".repeat(55)); + for student in &self.students { + println!( + " {:>5} {:<20} {:>6} {:<10} {:.1}", + student.id, + student.name, + student.age, + student.grade.as_str(), + student.score, + ); + } + } + + pub fn get_student(&self, id: Uuid) { + if self.students.is_empty(){ + println!("Invalid ID"); + return; + } + + for i in &self.students{ + if i.id == id { + println!(" + Student id: {}, + Student name: {}, + Student age: {}, + Student grade: {:?}, + Student score: {}, + ", i.id, i.name, i.age, i.grade, i.score) + }else { + println!("Invalid ID") + } + } + } + + pub fn update_name(&mut self, id: Uuid, new_name: &str) { + if let Some(index) = self.students.iter().position(|student| student.id == id) { + let student = &mut self.students[index]; + student.name = new_name.to_string(); + } + } + + pub fn update_age(&mut self, id: Uuid, new_age: u32) { + if let Some(index) = self.students.iter().position(|student| student.id == id) { + let student = &mut self.students[index]; + student.age = new_age; + } + } + + pub fn update_score(&mut self, id: Uuid, new_score: f32) { + if let Some(index) = self.students.iter().position(|student| student.id == id) { + let student = &mut self.students[index]; + student.score = new_score; + } + } + + pub fn update_grade(&mut self, id: Uuid, input: &str) { + if let Some(index) = self.students.iter().position(|student| student.id == id ){ + let student = &mut self.students[index]; + if input.to_lowercase().contains("first"){ + student.grade = Grade::First + } + if input.to_lowercase().contains("second"){ + student.grade = Grade::Second + } + if input.to_lowercase().contains("third"){ + student.grade = Grade::Third + } + } + } + + pub fn delete_student(&mut self, id: Uuid) { + if let Some(index) = self.students.iter().position(|student| student.id == id) { + self.students.remove(index); + } + } +} diff --git a/rust_session/assignments/student_registry_v2/src/registry_struct.rs b/rust_session/assignments/student_registry_v2/src/registry_struct.rs new file mode 100644 index 00000000..8b076a03 --- /dev/null +++ b/rust_session/assignments/student_registry_v2/src/registry_struct.rs @@ -0,0 +1,6 @@ +use crate::student_struct; + +pub struct Registry { + students: Vec, // Vec = "a list of Student values" + next_id: u32, // auto-increment counter for IDs +} \ No newline at end of file diff --git a/rust_session/assignments/student_registry_v2/src/student_struct.rs b/rust_session/assignments/student_registry_v2/src/student_struct.rs new file mode 100644 index 00000000..9211f866 --- /dev/null +++ b/rust_session/assignments/student_registry_v2/src/student_struct.rs @@ -0,0 +1,43 @@ +use crate::grade::{Grade, Sex}; +use uuid::Uuid; + +// A struct groups related pieces of data under one name. +// Think of it as a custom data type you design yourself. + +#[derive(Debug)] +pub struct Student { + pub id: Uuid, // u32 = unsigned 32-bit integer (no negatives) + pub name: String, // String = heap-allocated, growable text + pub age: u32, + pub sex: Sex, + pub grade: Grade, // our own enum type from above + pub score: f32, +} + +// This is the implementation of the student struct with its corresponding methods +impl Student { + pub fn new(id: Uuid, name: String, age: u32, sex: Sex, grade: Grade, score: f32) -> Student { + Student { + id, + name, + age, + sex, + grade, + score, + } + } +} + +#[derive(Debug)] +pub enum Status { + Pending, + Ongoing, + Completed, +} + +pub struct Todo { + id: u8, + title: String, + description: String, + status: Status, +} diff --git a/rust_session/assignments/student_registry_v2/src/utils.rs b/rust_session/assignments/student_registry_v2/src/utils.rs new file mode 100644 index 00000000..ab2c8eeb --- /dev/null +++ b/rust_session/assignments/student_registry_v2/src/utils.rs @@ -0,0 +1,3 @@ +// pub fn to_str(x: String) -> &'static str { +// x.as_str() +// }