diff --git a/rust_session/Assignemnt/student_registry/Cargo.lock b/rust_session/Assignemnt/student_registry/Cargo.lock new file mode 100644 index 00000000..e7f8ee38 --- /dev/null +++ b/rust_session/Assignemnt/student_registry/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "student_registry" +version = "0.1.0" diff --git a/rust_session/Assignemnt/student_registry/Cargo.toml b/rust_session/Assignemnt/student_registry/Cargo.toml new file mode 100644 index 00000000..bde0b676 --- /dev/null +++ b/rust_session/Assignemnt/student_registry/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "student_registry" +version = "0.1.0" +edition = "2021" diff --git a/rust_session/Assignemnt/student_registry/README.md b/rust_session/Assignemnt/student_registry/README.md new file mode 100644 index 00000000..9441b751 --- /dev/null +++ b/rust_session/Assignemnt/student_registry/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/Assignemnt/student_registry/notes/borrowing_deep_dive.md b/rust_session/Assignemnt/student_registry/notes/borrowing_deep_dive.md new file mode 100644 index 00000000..76411372 --- /dev/null +++ b/rust_session/Assignemnt/student_registry/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/Assignemnt/student_registry/notes/core_logic.md b/rust_session/Assignemnt/student_registry/notes/core_logic.md new file mode 100644 index 00000000..5383ef4e --- /dev/null +++ b/rust_session/Assignemnt/student_registry/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/Assignemnt/student_registry/notes/referencing_deep_dive.md b/rust_session/Assignemnt/student_registry/notes/referencing_deep_dive.md new file mode 100644 index 00000000..c422de29 --- /dev/null +++ b/rust_session/Assignemnt/student_registry/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/Assignemnt/student_registry/notes/struct.md b/rust_session/Assignemnt/student_registry/notes/struct.md new file mode 100644 index 00000000..162fa495 --- /dev/null +++ b/rust_session/Assignemnt/student_registry/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/Assignemnt/student_registry/src/grade.rs b/rust_session/Assignemnt/student_registry/src/grade.rs new file mode 100644 index 00000000..52a0cbb5 --- /dev/null +++ b/rust_session/Assignemnt/student_registry/src/grade.rs @@ -0,0 +1,31 @@ +#[derive(Debug, PartialEq, Clone)] +pub enum Grade { + First, + Second, + Third, +} + +impl Grade { + pub fn as_str(&self) -> &str { + match self { + Grade::First => "Cohort 1", + Grade::Second => "Cohort 2", + Grade::Third => "Cohort 3", + } + } +} + +#[derive(Debug, Clone)] +pub enum Sex { + Male, + Female, +} + +impl Sex { + pub fn to_str(&self) { + match self { + Sex::Male => println!("male: 👨🏾"), + Sex::Female => println!("female: 👧🏾"), + } + } +} diff --git a/rust_session/Assignemnt/student_registry/src/main.rs b/rust_session/Assignemnt/student_registry/src/main.rs new file mode 100644 index 00000000..ee9251b8 --- /dev/null +++ b/rust_session/Assignemnt/student_registry/src/main.rs @@ -0,0 +1,66 @@ +mod grade; +mod registry; +mod student_struct; +mod update_field; +mod utils; + +use grade::{Grade, Sex}; +use registry::Registry; +use student_struct::Student; +use update_field::UpdateField; + +fn main() { + let mut registry = Registry::new(); + + // Add a student + registry.add("Alice", 17, Sex::Female, Grade::First, 86.6); + + registry.add("Davina", 17, Sex::Female, Grade::First, 86.6); + + registry.add("Mimi", 17, Sex::Female, Grade::Third, 86.6); + + registry.add("Blessing", 17, Sex::Female, Grade::Second, 86.6); + + // List all students + registry.list_all(); + + println!(""); + + registry.get_student(1); + registry.get_student(2); + registry.get_student(3); + registry.get_student(4); + + println!("----------------------------------------------"); + + println!("------------Update students name----------"); + // Update a student's name (ID 1) + registry.update_data(1, UpdateField::Name("Sonia".to_string())); + registry.update_data(2, UpdateField::Name("David".to_string())); + registry.update_data(3, UpdateField::Name("Joy".to_string())); + registry.update_data(4, UpdateField::Name("Favour".to_string())); + + println!(" -----------Update students age---------- "); + // update a students score + registry.update_data(1, UpdateField::Age(20)); + registry.update_data(2, UpdateField::Age(24)); + registry.update_data(3, UpdateField::Age(28)); + registry.update_data(4, UpdateField::Age(32)); + + println!("------------Update students score----------"); + registry.update_data(1, UpdateField::Score(94.8)); + registry.update_data(2, UpdateField::Score(20.5)); + registry.update_data(3, UpdateField::Score(60.2)); + registry.update_data(4, UpdateField::Score(70.3)); + + println!("-----------List all students agian to confirm update-------------"); + registry.list_all(); + + // registry.delete_student(1); + // registry.delete_student(2); + // registry.delete_student(3); + // registry.delete_student(4); + + // println!("-----------List all students agian to confirm delete-------------"); + // registry.list_all(); +} diff --git a/rust_session/Assignemnt/student_registry/src/registry.rs b/rust_session/Assignemnt/student_registry/src/registry.rs new file mode 100644 index 00000000..17478e5d --- /dev/null +++ b/rust_session/Assignemnt/student_registry/src/registry.rs @@ -0,0 +1,75 @@ +use crate::grade::{Grade, Sex}; +use crate::student_struct::Student; +use crate::update_field::UpdateField; + +pub struct Registry { + pub students: Vec, + next_id: u32, +} + +impl Registry { + pub fn new() -> Self { + Registry { + students: Vec::new(), + next_id: 1, + } + } + + pub fn add(&mut self, name: &str, age: u8, sex: Sex, grade: Grade, score: f32) { + let id = self.next_id; + let student = Student::new(id, name.to_string(), age, sex, grade, 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) { + match self.students.iter().find(|s| s.id == id) { + Some(student) => println!( + "ID: {} | Name: {} | Age: {} | Sex: {:?} | Grade: {:?} | Score: {}" , + student.id, student.name, student.age,student.sex, student.grade, student.score + ), + None => println!("No student found with ID {}", id), + } + } + + // This function takes in the student ID to know which student and takes in fields which type is the updateFiled Enum + // So that it knows what to change. + pub fn update_data(&mut self, id: u32, field: UpdateField) { + match self.students.iter_mut().find(|s| s.id == id) { + Some(student_data) => { + println!("Update {} for student ID {}", field.label(), id); + field.apply(student_data); + } + + None => println!("No Student Found with this id {} ", id), + } + } + + // It goes through the vector and keeps every student where the condition is true, and removes any student where it's false. + pub fn delete_student(&mut self, id: u32) { + self.students.retain(|student| student.id != id); + } +} diff --git a/rust_session/Assignemnt/student_registry/src/registry_struct.rs b/rust_session/Assignemnt/student_registry/src/registry_struct.rs new file mode 100644 index 00000000..8b076a03 --- /dev/null +++ b/rust_session/Assignemnt/student_registry/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/Assignemnt/student_registry/src/student_struct.rs b/rust_session/Assignemnt/student_registry/src/student_struct.rs new file mode 100644 index 00000000..f3921f40 --- /dev/null +++ b/rust_session/Assignemnt/student_registry/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)] +pub struct Student { + pub id: u32, // u32 = unsigned 32-bit integer (no negatives) + pub name: String, // String = heap-allocated, growable text + pub age: u8, + 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: u8, 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/Assignemnt/student_registry/src/update_field.rs b/rust_session/Assignemnt/student_registry/src/update_field.rs new file mode 100644 index 00000000..a4ee25c1 --- /dev/null +++ b/rust_session/Assignemnt/student_registry/src/update_field.rs @@ -0,0 +1,39 @@ + +use crate::grade::{Grade, Sex}; +use crate::student_struct::Student; + +#[derive(Debug)] +pub enum UpdateField { + Name(String), + Age(u8), + Sex(Sex), + Grade(Grade), + Score(f32), +} + +impl UpdateField { + // This function describes the fields being changed (Useful for logging/feedback) + pub fn label(&self) -> &str { + match self { + UpdateField::Name(_) => "name", + UpdateField::Age(_) => "age", + UpdateField::Sex(_) => "gender", + UpdateField::Grade(_) => "grade", + UpdateField::Score(_) => "score", + } + } + + // This function applies the changes directly onto a student. + // What this function does is: I have a package... I want to change a value in that package. + // apply() unwraps the package and write the new value unto the student. + pub fn apply(&self, student: &mut Student) { + match self { + // takes the value i'm carrying and writes it onto student. + UpdateField::Name(val) => student.name = val.clone(), + UpdateField::Age(val) => student.age = *val, + UpdateField::Sex(val) => student.sex = val.clone(), + UpdateField::Grade(val) => student.grade = val.clone(), + UpdateField::Score(val) => student.score = *val, + } + } +} diff --git a/rust_session/Assignemnt/student_registry/src/utils.rs b/rust_session/Assignemnt/student_registry/src/utils.rs new file mode 100644 index 00000000..ab2c8eeb --- /dev/null +++ b/rust_session/Assignemnt/student_registry/src/utils.rs @@ -0,0 +1,3 @@ +// pub fn to_str(x: String) -> &'static str { +// x.as_str() +// } diff --git a/rust_session/Assignemnt/student_registry_UUID/Cargo.lock b/rust_session/Assignemnt/student_registry_UUID/Cargo.lock new file mode 100644 index 00000000..e33a95c8 --- /dev/null +++ b/rust_session/Assignemnt/student_registry_UUID/Cargo.lock @@ -0,0 +1,500 @@ +# 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.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[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 = "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.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" + +[[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 = [ + "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 = "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/Assignemnt/student_registry_UUID/Cargo.toml b/rust_session/Assignemnt/student_registry_UUID/Cargo.toml new file mode 100644 index 00000000..65957bb6 --- /dev/null +++ b/rust_session/Assignemnt/student_registry_UUID/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "student_registry" +version = "0.1.0" +edition = "2021" +[dependencies] +uuid = { version = "1.6", features = ["v4"] } \ No newline at end of file diff --git a/rust_session/Assignemnt/student_registry_UUID/README.md b/rust_session/Assignemnt/student_registry_UUID/README.md new file mode 100644 index 00000000..9441b751 --- /dev/null +++ b/rust_session/Assignemnt/student_registry_UUID/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/Assignemnt/student_registry_UUID/notes/borrowing_deep_dive.md b/rust_session/Assignemnt/student_registry_UUID/notes/borrowing_deep_dive.md new file mode 100644 index 00000000..76411372 --- /dev/null +++ b/rust_session/Assignemnt/student_registry_UUID/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/Assignemnt/student_registry_UUID/notes/core_logic.md b/rust_session/Assignemnt/student_registry_UUID/notes/core_logic.md new file mode 100644 index 00000000..5383ef4e --- /dev/null +++ b/rust_session/Assignemnt/student_registry_UUID/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/Assignemnt/student_registry_UUID/notes/referencing_deep_dive.md b/rust_session/Assignemnt/student_registry_UUID/notes/referencing_deep_dive.md new file mode 100644 index 00000000..c422de29 --- /dev/null +++ b/rust_session/Assignemnt/student_registry_UUID/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/Assignemnt/student_registry_UUID/notes/struct.md b/rust_session/Assignemnt/student_registry_UUID/notes/struct.md new file mode 100644 index 00000000..162fa495 --- /dev/null +++ b/rust_session/Assignemnt/student_registry_UUID/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/Assignemnt/student_registry_UUID/src/grade.rs b/rust_session/Assignemnt/student_registry_UUID/src/grade.rs new file mode 100644 index 00000000..52a0cbb5 --- /dev/null +++ b/rust_session/Assignemnt/student_registry_UUID/src/grade.rs @@ -0,0 +1,31 @@ +#[derive(Debug, PartialEq, Clone)] +pub enum Grade { + First, + Second, + Third, +} + +impl Grade { + pub fn as_str(&self) -> &str { + match self { + Grade::First => "Cohort 1", + Grade::Second => "Cohort 2", + Grade::Third => "Cohort 3", + } + } +} + +#[derive(Debug, Clone)] +pub enum Sex { + Male, + Female, +} + +impl Sex { + pub fn to_str(&self) { + match self { + Sex::Male => println!("male: 👨🏾"), + Sex::Female => println!("female: 👧🏾"), + } + } +} diff --git a/rust_session/Assignemnt/student_registry_UUID/src/main.rs b/rust_session/Assignemnt/student_registry_UUID/src/main.rs new file mode 100644 index 00000000..92398913 --- /dev/null +++ b/rust_session/Assignemnt/student_registry_UUID/src/main.rs @@ -0,0 +1,50 @@ +mod grade; +mod registry; +mod student_struct; +mod update_field; +mod utils; + +use grade::{Grade, Sex}; +use registry::Registry; +use student_struct::Student; +use update_field::UpdateField; +// use uuid::Uuid; + +fn main() { + let mut registry = Registry::new(); + + // Add a student + let alice_id = registry.add("Alice", 17, Sex::Female, Grade::First, 86.6); + + let davina_id = registry.add("Davina", 17, Sex::Female, Grade::First, 86.6); + + let mimi_id = registry.add("Mimi", 17, Sex::Female, Grade::Third, 86.6); + + let blessing_id = registry.add("Blessing", 17, Sex::Female, Grade::Second, 86.6); + + // List all students + registry.list_all(); + + println!("------------Update students name----------"); + // Update a student's name (ID 1) + registry.update_data(alice_id, UpdateField::Name("Sonia".to_string())); + registry.update_data(davina_id, UpdateField::Name("David".to_string())); + registry.update_data(mimi_id, UpdateField::Name("Joy".to_string())); + registry.update_data(blessing_id, UpdateField::Name("Favour".to_string())); + + println!(" -----------Update students age---------- "); + // update a students score + registry.update_data(alice_id, UpdateField::Age(20)); + registry.update_data(davina_id, UpdateField::Age(24)); + registry.update_data(mimi_id, UpdateField::Age(28)); + registry.update_data(blessing_id, UpdateField::Age(32)); + + println!("------------Update students score----------"); + registry.update_data(alice_id, UpdateField::Score(94.8)); + registry.update_data(davina_id, UpdateField::Score(20.5)); + registry.update_data(mimi_id, UpdateField::Score(60.2)); + registry.update_data(blessing_id, UpdateField::Score(70.3)); + + println!("-----------List all students agian to confirm update-------------"); + registry.list_all(); +} diff --git a/rust_session/Assignemnt/student_registry_UUID/src/registry.rs b/rust_session/Assignemnt/student_registry_UUID/src/registry.rs new file mode 100644 index 00000000..50dcae69 --- /dev/null +++ b/rust_session/Assignemnt/student_registry_UUID/src/registry.rs @@ -0,0 +1,75 @@ + +use crate::grade::{Grade, Sex}; +use crate::student_struct::Student; +use crate::update_field::UpdateField; +use uuid::Uuid; + + +pub struct Registry { + pub students: Vec +} + +impl Registry { + pub fn new() -> Self { + Registry { + students: Vec::new() } + } + + pub fn add(&mut self, name: &str, age: u8, sex: Sex, grade: Grade, score: f32) -> Uuid { + let id = Uuid::new_v4(); // generates a unique ID automatically + let student = Student::new(id, name.to_string(), age, sex, grade, score); + println!("Added: {} (ID {})", student.name, student.id); + self.students.push(student); + id // return the id so the caller can store it + } + + 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) { + match self.students.iter().find(|s| s.id == id) { + Some(student) => println!( + "ID: {} | Name: {} | Age: {} | Grade: {:?} | Score: {}", + student.id, student.name, student.age, student.grade, student.score + ), + None => println!("No student found with ID {}", id), + } + } + + // This function takes in the student ID to know which student and takes in fields which type is the updateFiled Enum + // So that it knows what to change. + pub fn update_data(&mut self, id: Uuid, field: UpdateField) { + match self.students.iter_mut().find(|s| s.id == id) { + Some(student_data) => { + println!("Update {} for student ID {}", field.label(), id); + field.apply(student_data); + } + + None => println!("No Student Found with this id {} ", id), + } + } + + pub fn delete_student(&mut self, id: Uuid) { + // It goes through the vector and keeps every student where the condition is true, and removes any student where it's false. + self.students.retain(|student| student.id != id); + } +} diff --git a/rust_session/Assignemnt/student_registry_UUID/src/registry_struct.rs b/rust_session/Assignemnt/student_registry_UUID/src/registry_struct.rs new file mode 100644 index 00000000..8b076a03 --- /dev/null +++ b/rust_session/Assignemnt/student_registry_UUID/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/Assignemnt/student_registry_UUID/src/student_struct.rs b/rust_session/Assignemnt/student_registry_UUID/src/student_struct.rs new file mode 100644 index 00000000..8685a208 --- /dev/null +++ b/rust_session/Assignemnt/student_registry_UUID/src/student_struct.rs @@ -0,0 +1,44 @@ + +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: u8, + 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: u8, 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/Assignemnt/student_registry_UUID/src/update_field.rs b/rust_session/Assignemnt/student_registry_UUID/src/update_field.rs new file mode 100644 index 00000000..4a329f16 --- /dev/null +++ b/rust_session/Assignemnt/student_registry_UUID/src/update_field.rs @@ -0,0 +1,38 @@ +use crate::grade::{Grade, Sex}; +use crate::student_struct::Student; + +#[derive(Debug)] +pub enum UpdateField { + Name(String), + Age(u8), + Sex(Sex), + Grade(Grade), + Score(f32), +} + +impl UpdateField { + // This function describes the fields being changed (Useful for logging/feedback) + pub fn label(&self) -> &str { + match self { + UpdateField::Name(_) => "name", + UpdateField::Age(_) => "age", + UpdateField::Sex(_) => "gender", + UpdateField::Grade(_) => "grade", + UpdateField::Score(_) => "score", + } + } + + // This function applies the changes directly onto a student. + // What this function does is: I have a package... I want to change a value in that package. + // apply() unwraps the package and write the new value unto the student. + pub fn apply(&self, student: &mut Student) { + match self { + // takes the value i'm carrying and writes it onto student. + UpdateField::Name(val) => student.name = val.clone(), + UpdateField::Age(val) => student.age = *val, + UpdateField::Sex(val) => student.sex = val.clone(), + UpdateField::Grade(val) => student.grade = val.clone(), + UpdateField::Score(val) => student.score = *val, + } + } +} diff --git a/rust_session/Assignemnt/student_registry_UUID/src/utils.rs b/rust_session/Assignemnt/student_registry_UUID/src/utils.rs new file mode 100644 index 00000000..ab2c8eeb --- /dev/null +++ b/rust_session/Assignemnt/student_registry_UUID/src/utils.rs @@ -0,0 +1,3 @@ +// pub fn to_str(x: String) -> &'static str { +// x.as_str() +// }