From b402f6d465165dcb5cd60b481d59bc8cf8275c4f Mon Sep 17 00:00:00 2001 From: luhrhenz Date: Wed, 10 Jun 2026 15:41:14 +0100 Subject: [PATCH 1/3] chore: added update, delete and registry_v2 --- rust_session/src/grade.rs | 31 ++ rust_session/src/main.rs | 35 +- rust_session/src/student_struct.rs | 37 ++ rust_session/src/voters.rs | 35 ++ rust_session/student_registry/src/main.rs | 59 ++- rust_session/student_registry/src/registry.rs | 31 ++ rust_session/student_registry_v2/Cargo.lock | 500 ++++++++++++++++++ rust_session/student_registry_v2/Cargo.toml | 7 + .../student_registry_v2/notes/explanation.md | 344 ++++++++++++ rust_session/student_registry_v2/src/grade.rs | 31 ++ rust_session/student_registry_v2/src/main.rs | 49 ++ .../student_registry_v2/src/registry.rs | 76 +++ .../student_registry_v2/src/student_struct.rs | 24 + 13 files changed, 1230 insertions(+), 29 deletions(-) create mode 100644 rust_session/src/grade.rs create mode 100644 rust_session/src/student_struct.rs create mode 100644 rust_session/src/voters.rs create mode 100644 rust_session/student_registry_v2/Cargo.lock create mode 100644 rust_session/student_registry_v2/Cargo.toml create mode 100644 rust_session/student_registry_v2/notes/explanation.md create mode 100644 rust_session/student_registry_v2/src/grade.rs create mode 100644 rust_session/student_registry_v2/src/main.rs create mode 100644 rust_session/student_registry_v2/src/registry.rs create mode 100644 rust_session/student_registry_v2/src/student_struct.rs diff --git a/rust_session/src/grade.rs b/rust_session/src/grade.rs new file mode 100644 index 00000000..fbbfd838 --- /dev/null +++ b/rust_session/src/grade.rs @@ -0,0 +1,31 @@ +#[derive(Debug)] +pub enum Grade { + First, + Second, + Third +} + +impl Grade { + pub fn get_grade_points(&self) -> &str { + match self { + Grade::First => "Cohort 1", + Grade::Second => "Cohort 2", + Grade::Third => "Cohort 3", + } + } +} + +#[derive(Debug)] +pub enum Sex { + Male, + Female +} + +impl Sex { + pub fn get_sex(&self) { + match self { + Sex::Male => println!("Male"), + Sex::Female => println!("Female"), + } + } +} \ No newline at end of file diff --git a/rust_session/src/main.rs b/rust_session/src/main.rs index c11cc20e..9dd41a56 100644 --- a/rust_session/src/main.rs +++ b/rust_session/src/main.rs @@ -1,17 +1,38 @@ -mod sub; -mod sum; -mod ownership; -mod array; -mod mut_ex; - +// mod sub; +// mod sum; +// mod ownership; +// mod array; +// mod mut_ex; +// mod grade; +// mod voters; +// mod student_struct; fn main() { // use sub::sub; + // use crate::voters::{Age, EligibleVoters}; // sub(10, 5); // ownership::test_ownership(); // ownership::call_name(); // array::test_array(); // ownership::test_move(); // ownership::call_greet(); - mut_ex::test_mut(); + // mut_ex::test_mut(); + // grade::Grade::First.get_grade_points(); + // grade::Grade::Second.get_grade_points(); + // grade::Grade::Third.get_grade_points(); + // grade::Sex::Male.get_sex(); + // grade::Sex::Female.get_sex(); + // voters::Age::SilverJubilee.get_age(); + // voters::Age::GoldenJubilee.get_age(); + // voters::Age::PlatinumJubilee.get_age(); + // voters::Age::Centenarian.get_age(); + // voters::EligibleVoters::Juvenile.get_eligible_voters(); + // voters::EligibleVoters::Teenager.get_eligible_voters(); + // voters::EligibleVoters::Adult.get_eligible_voters(); + + // let age = Age::SilverJubilee; + // let eligible_voters = EligibleVoters::Teenager; + // println!("Age: {}", age.get_age()); + // println!("Eligible Voters: {}", eligible_voters.get_eligible_voters()); + } diff --git a/rust_session/src/student_struct.rs b/rust_session/src/student_struct.rs new file mode 100644 index 00000000..67ee21cd --- /dev/null +++ b/rust_session/src/student_struct.rs @@ -0,0 +1,37 @@ +#[derive(Debug)] +pub enum Status{ + Pending, + Completed, + InProgress, + Cancelled, +} + +impl Status { + pub fn get_status(&self) -> &str { + match self { + Status::Pending => "Pending", + Status::Completed => "Completed", + Status::InProgress => "InProgress", + Status::Cancelled => "Cancelled", + } + } +} + + +pub struct Todo { + pub id: u8, + pub title: String, + pub description: String, + pub status: Status, +} + +impl Todo { + pub fn new(id: u8, title: String, description: String, status: Status) -> Todo { + Todo { + id, + title, + description, + status + } + } +} \ No newline at end of file diff --git a/rust_session/src/voters.rs b/rust_session/src/voters.rs new file mode 100644 index 00000000..2d6a3918 --- /dev/null +++ b/rust_session/src/voters.rs @@ -0,0 +1,35 @@ +#[derive(Debug)] +pub enum Age { + SilverJubilee, + GoldenJubilee, + PlatinumJubilee, + Centenarian +} + +impl Age { + pub fn get_age(&self) -> &str { + match self { + Age::SilverJubilee => "25 years", + Age::GoldenJubilee => "50 years", + Age::PlatinumJubilee => "75 years", + Age::Centenarian => "100 years", + } + } +} + +#[derive(Debug)] +pub enum EligibleVoters { + Juvenile, + Teenager, + Adult +} + +impl EligibleVoters { + pub fn get_eligible_voters(&self) -> &str { + match self { + EligibleVoters::Juvenile => "17 years and below", + EligibleVoters::Teenager => "18 years and above", + EligibleVoters::Adult => "35 years and above", + } + } +} \ No newline at end of file diff --git a/rust_session/student_registry/src/main.rs b/rust_session/student_registry/src/main.rs index 7ae18ff7..c4076c08 100644 --- a/rust_session/student_registry/src/main.rs +++ b/rust_session/student_registry/src/main.rs @@ -1,11 +1,11 @@ mod grade; mod registry; mod student_struct; -mod utils; +// mod utils; use grade::{Grade, Sex}; use registry::Registry; -use student_struct::Student; +// use student_struct::Student; fn main() { // let g = Grade::Second; @@ -15,29 +15,44 @@ fn main() { // let ss = Student::new(); // println!("stund") - // let mut reg = Registry::new(); + let mut reg = Registry::new(); + + reg.add("Victor", 20, Sex::Male, Grade::First, 78.5,); + reg.add("Kosi", 22, Sex::Female, Grade::Second, 64.0); + reg.add("Yusrah", 21, Sex::Female, Grade::First, 91.0); - // reg.add("Victor", 20, Grade::First, 78.5); - // reg.add("Kosi", 22, Grade::Second, 64.0); - // reg.add("Yusrah", 21, Grade::First, 91.0); + reg.list_all(); - // reg.list_all();] - let sex = Sex::Male; - println!("sex: {:?}", sex.to_str()); + let second_id = reg.students[1].id.clone(); - // let s: Student = Student::new(1, String::from("Testimony"), 16, Sex::Female, Grade::Third, 40.5); - let s: Student = Student::new( - 1, - "Testimony".to_string(), - 16, - Sex::Female, - Grade::Third, - 40.5, - ); - println!("student here: {:#?}", s); + match reg.find_by_id(second_id) { + Some(student) => println!("Found student: {} with ID: {}", student.name, student.id), + None => println!("Student not found"), + } - println!("student id: {}", s.id); - println!("student name: {}", s.name); - println!("student age: {}", s.age); + reg.update(second_id, "joy", 25, 50.0); + println!(); + reg.list_all(); + + + + + // let sex = Sex::Male; + // println!("sex: {:?}", sex.to_str()); + + // // let s: Student = Student::new(1, String::from("Testimony"), 16, Sex::Female, Grade::Third, 40.5); + // let s: Student = Student::new( + // 1, + // "Testimony".to_string(), + // 16, + // Sex::Female, + // Grade::Third, + // 40.5, + // ); + // println!("student here: {:#?}", s); + + // println!("student id: {}", s.id); + // println!("student name: {}", s.name); + // println!("student age: {}", s.age); } diff --git a/rust_session/student_registry/src/registry.rs b/rust_session/student_registry/src/registry.rs index 146dc186..83a9ae1f 100644 --- a/rust_session/student_registry/src/registry.rs +++ b/rust_session/student_registry/src/registry.rs @@ -7,6 +7,13 @@ pub struct Registry { } impl Registry { + pub fn new() -> Registry { + Registry { + students: Vec::new(), + next_id: 0, + } + } + 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); @@ -36,4 +43,28 @@ impl Registry { ); } } + + pub fn find_by_id(&self, id: u32) -> Option<&Student> { + self.students.iter().find(|s| s.id == id) + } + // - .iter() loops through the vec without taking ownership + // - .find() returns the first match wrapped in Option<&Student> + // - Option means it can be Some(student) if found, or None if not — Rust forces you to handle both cases, no null panics + + pub fn delete(&mut self, id: u32) { + self.students.retain(|s| s.id != id); + } + // - .retain() keeps only elements where the condition is true + // - So s.id != id means "keep everyone whose id is NOT the one we want to remove" + // - It mutates the vec in place — no need to find an index + + pub fn update(&mut self, id: u32, name: &str, age: u8, score: f32) { + if let Some(student) = self.students.iter_mut().find(|s| s.id == id) { + student.name = name.to_string(); + student.age = age; + student.score = score; + } + } + // - .iter_mut() gives mutable references so you can modify fields in place + // - if let Some(student) is Rust's clean way to say "if found, do this, otherwise skip" } diff --git a/rust_session/student_registry_v2/Cargo.lock b/rust_session/student_registry_v2/Cargo.lock new file mode 100644 index 00000000..a4be051f --- /dev/null +++ b/rust_session/student_registry_v2/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_v2" +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/student_registry_v2/Cargo.toml b/rust_session/student_registry_v2/Cargo.toml new file mode 100644 index 00000000..1bf734cd --- /dev/null +++ b/rust_session/student_registry_v2/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "student_registry_v2" +version = "0.1.0" +edition = "2021" + +[dependencies] +uuid = { version = "1", features = ["v4"] } diff --git a/rust_session/student_registry_v2/notes/explanation.md b/rust_session/student_registry_v2/notes/explanation.md new file mode 100644 index 00000000..c66d38cd --- /dev/null +++ b/rust_session/student_registry_v2/notes/explanation.md @@ -0,0 +1,344 @@ +# Student Registry V2 — Code Explanation + +## What is this project? + +This is a simple in-memory student registry built in Rust. +"In-memory" means the data only lives while the program is running — nothing is saved to a file or database. + +The big difference from v1 is that instead of giving each student a simple number as their ID (`1`, `2`, `3`...), +we use a **UUID** — a randomly generated unique string like `f3dbcfa8-a037-4a8e-82d8-21b4b6235c2d`. + +--- + +## Project Structure + +``` +student_registry_v2/ +├── Cargo.toml ← project config + dependencies +└── src/ + ├── main.rs ← entry point, where execution starts + ├── grade.rs ← Grade and Sex enums + ├── student_struct.rs ← the Student data type + └── registry.rs ← the Registry that manages students +``` + +--- + +## File by File + +--- + +### `Cargo.toml` + +```toml +[dependencies] +uuid = { version = "1", features = ["v4"] } +``` + +This tells Cargo (Rust's package manager) to download and include the `uuid` library. +The `features = ["v4"]` part means we want version 4 UUIDs — the kind that are **randomly generated**. + +--- + +### `grade.rs` — Enums + +```rust +pub enum Grade { + First, + Second, + Third, +} +``` + +An **enum** is a type that can only be one of a fixed set of values. +Think of it like a multiple-choice answer — a student's grade can only be First, Second, or Third, nothing else. + +```rust +impl Grade { + pub fn as_str(&self) -> &str { + match self { + Grade::First => "Cohort 1", + Grade::Second => "Cohort 2", + Grade::Third => "Cohort 3", + } + } +} +``` + +`impl Grade` adds methods to the enum — just like adding functions that belong to it. +`as_str()` takes the current value (`&self`) and uses `match` to return its text label. +`match` is Rust's version of a switch statement, but it must cover every possible case. + +`&str` is a borrowed string slice — a reference to text. It doesn't own the string, just points to it. + +```rust +pub enum Sex { + Male, + Female, +} +``` + +Same idea — Sex can only be Male or Female. + +```rust +impl Sex { + pub fn to_str(&self) -> &str { + match self { + Sex::Male => "male", + Sex::Female => "female", + } + } +} +``` + +--- + +### `student_struct.rs` — The Student + +```rust +#[derive(Debug)] +pub struct Student { + pub id: String, + pub name: String, + pub age: u8, + pub sex: Sex, + pub grade: Grade, + pub score: f32, +} +``` + +A **struct** is a custom data type that groups related fields together. +Think of it like a form — every student has the same fields, but different values. + +| Field | Type | Meaning | +|---------|----------|----------------------------------------------| +| `id` | `String` | UUID — unique identifier (random string) | +| `name` | `String` | Heap-allocated text, owned by this struct | +| `age` | `u8` | Unsigned 8-bit integer (0–255), enough for age | +| `sex` | `Sex` | Our own enum from grade.rs | +| `grade` | `Grade` | Our own enum from grade.rs | +| `score` | `f32` | 32-bit floating point number (e.g. 78.5) | + +`#[derive(Debug)]` automatically gives the struct the ability to be printed with `{:?}` or `{:#?}`. + +`pub` on each field means code outside this file can read and write those fields directly. + +```rust +impl Student { + pub fn new(id: String, name: String, age: u8, sex: Sex, grade: Grade, score: f32) -> Student { + Student { id, name, age, sex, grade, score } + } +} +``` + +`new()` is a constructor — a function that builds and returns a `Student`. +Rust has no built-in constructor keyword, so by convention we write a `new()` function. +`-> Student` means this function returns a `Student` value. + +--- + +### `registry.rs` — The Registry (the core logic) + +```rust +pub struct Registry { + pub students: Vec, +} +``` + +`Vec` is a growable list of students — like an array that can expand. +In v1 there was also a `next_id: u32` counter here. In v2 it's gone — UUID handles its own uniqueness. + +--- + +#### `new()` +```rust +pub fn new() -> Registry { + Registry { + students: Vec::new(), + } +} +``` +Creates an empty registry with an empty list. `Vec::new()` gives you an empty vector. + +--- + +#### `add()` +```rust +pub fn add(&mut self, name: &str, age: u8, sex: Sex, grade: Grade, score: f32) { + let id = Uuid::new_v4().to_string(); + let student = Student::new(id, name.to_string(), age, sex, grade, score); + println!("Added: {} (ID: {})", student.name, student.id); + self.students.push(student); +} +``` + +- `&mut self` — the registry is borrowed mutably, meaning we're allowed to change it +- `Uuid::new_v4()` — generates a random UUID (128-bit number) +- `.to_string()` — converts it into a regular `String` +- `name.to_string()` — `name` comes in as `&str` (a reference), we convert it to an owned `String` so the student can own its name +- `.push(student)` — appends the student to the end of the vector + +--- + +#### `list_all()` +```rust +pub fn list_all(&self) { + if self.students.is_empty() { ... } + for student in &self.students { ... } +} +``` + +- `&self` — borrowed immutably, we're only reading, not changing +- `&self.students` — we iterate over references to the students (we don't consume or move them) +- `for student in &self.students` — `student` here is `&Student`, a reference to each one + +--- + +#### `find_by_id()` +```rust +pub fn find_by_id(&self, id: &str) -> Option<&Student> { + self.students.iter().find(|s| s.id == id) +} +``` + +- `.iter()` — creates an iterator over references to the students +- `.find(|s| s.id == id)` — goes through each student and returns the first one where `s.id == id` +- `Option<&Student>` — the return type. In Rust there is no `null`. Instead: + - `Some(&student)` is returned if a match is found + - `None` is returned if nothing matches +- This forces you to handle both cases, preventing null pointer crashes + +--- + +#### `update()` +```rust +pub fn update(&mut self, id: &str, name: &str, age: u8, score: f32) { + if let Some(student) = self.students.iter_mut().find(|s| s.id == id) { + student.name = name.to_string(); + student.age = age; + student.score = score; + println!("Updated student with ID: {}", id); + } else { + println!("Student with ID {} not found", id); + } +} +``` + +- `.iter_mut()` — like `.iter()` but gives **mutable** references, so we can modify fields +- `if let Some(student)` — this is Rust's clean way of saying "if the Option has a value, unwrap it into `student` and run this block, otherwise go to `else`" +- We then directly reassign the fields on the found student + +--- + +#### `delete()` +```rust +pub fn delete(&mut self, id: &str) { + let before = self.students.len(); + self.students.retain(|s| s.id != id); + if self.students.len() < before { + println!("Deleted student with ID: {}", id); + } else { + println!("Student with ID {} not found", id); + } +} +``` + +- `.retain(|s| s.id != id)` — keeps only the students where the condition is `true` + - Students whose id does NOT match the given id are kept + - The one whose id matches is silently dropped +- We compare `len()` before and after to know if anything was actually removed + +--- + +### `main.rs` — Putting it all together + +```rust +mod grade; +mod registry; +mod student_struct; +``` + +`mod` tells Rust to include these files as modules. Without this, the files are invisible to the compiler. + +```rust +use grade::{Grade, Sex}; +use registry::Registry; +``` + +`use` brings names into scope so you don't have to write `grade::Grade` every time. + +```rust +let mut reg = Registry::new(); +``` + +`let` declares a variable. `mut` means it's mutable (changeable). Without `mut`, Rust won't let you call methods that modify it. + +```rust +reg.add("Victor", 20, Sex::Male, Grade::First, 78.5); +``` + +Adds Victor to the registry. Inside `add()`, a UUID is generated automatically for him. + +```rust +let first_id = reg.students[0].id.clone(); +``` + +Gets the id of the first student. `.clone()` is needed because `id` is a `String` — if we just wrote `reg.students[0].id`, we'd be moving it out of the struct (Rust won't allow that while the struct is still alive). `.clone()` makes a copy instead. + +```rust +match reg.find_by_id(&first_id) { + Some(s) => println!("Found: {:#?}", s), + None => println!("Not found"), +} +``` + +`match` on an `Option` is the standard Rust pattern. We handle both outcomes: +- `Some(s)` — found, print the student with pretty debug formatting (`{:#?}`) +- `None` — not found, print a message + +```rust +reg.update(&first_id, "Victor Updated", 21, 85.0); +``` + +Updates Victor's name, age, and score. The id stays the same — we're just changing the other fields. + +```rust +reg.delete(&first_id); +``` + +Removes Victor from the registry entirely. + +--- + +## v1 vs v2 — The key differences + +| | v1 | v2 | +|---|---|---| +| Student ID type | `u32` (number) | `String` (UUID) | +| ID generation | `next_id` counter in Registry | `Uuid::new_v4()` | +| Predictable IDs | yes (0, 1, 2...) | no (random each run) | +| Works across systems | no | yes | +| find_by_id | not implemented | `Option<&Student>` | +| update | not implemented | `.iter_mut()` + field assignment | +| delete | not implemented | `.retain()` | +| External dependency | none | `uuid` crate | + +--- + +## Key Rust Concepts Used + +| Concept | Where | What it means | +|---|---|---| +| `struct` | `Student`, `Registry` | Custom data type grouping fields | +| `enum` | `Grade`, `Sex` | A type with a fixed set of variants | +| `impl` | all structs/enums | Adds methods to a type | +| `Vec` | `students` field | A growable list | +| `Option` | `find_by_id` return | Either `Some(value)` or `None` — no nulls | +| `&self` | read-only methods | Borrow the value without taking ownership | +| `&mut self` | mutating methods | Borrow the value and allow changes | +| `.iter()` | `list_all`, `find_by_id` | Loop without consuming the collection | +| `.iter_mut()` | `update` | Loop with ability to modify elements | +| `.retain()` | `delete` | Keep only elements matching a condition | +| `.clone()` | `main.rs` | Make a deep copy of an owned value | +| `if let Some(x)` | `update` | Unwrap an Option only if it has a value | +| `match` | everywhere | Pattern match — must handle all cases | diff --git a/rust_session/student_registry_v2/src/grade.rs b/rust_session/student_registry_v2/src/grade.rs new file mode 100644 index 00000000..401b0c3a --- /dev/null +++ b/rust_session/student_registry_v2/src/grade.rs @@ -0,0 +1,31 @@ +#[derive(Debug, PartialEq)] +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)] +pub enum Sex { + Male, + Female, +} + +impl Sex { + pub fn to_str(&self) -> &str { + match self { + Sex::Male => "male", + Sex::Female => "female", + } + } +} diff --git a/rust_session/student_registry_v2/src/main.rs b/rust_session/student_registry_v2/src/main.rs new file mode 100644 index 00000000..be68411d --- /dev/null +++ b/rust_session/student_registry_v2/src/main.rs @@ -0,0 +1,49 @@ +mod grade; +mod registry; +mod student_struct; + +use grade::{Grade, Sex}; +use registry::Registry; + +fn section(title: &str) { + let width = 60; + println!("\n{}", "=".repeat(width)); + println!("{:^width$}", title, width = width); + println!("{}", "=".repeat(width)); +} + +fn main() { + let mut reg = Registry::new(); + + section("REGISTERING STUDENTS"); + reg.add("Victor", 20, Sex::Male, Grade::First, 78.5); + reg.add("Kosi", 22, Sex::Female, Grade::Second, 64.0); + reg.add("Yusrah", 21, Sex::Female, Grade::First, 91.0); + + section("ALL STUDENTS"); + reg.list_all(); + + let first_id = reg.students[0].id.clone(); + + // section("FIND BY ID"); + // match reg.find_by_id(&first_id) { + // Some(s) => { + // println!(" Name : {}", s.name); + // println!(" ID : {}", s.id); + // println!(" Age : {}", s.age); + // println!(" Sex : {}", s.sex.to_str()); + // println!(" Grade : {}", s.grade.as_str()); + // println!(" Score : {:.1}", s.score); + // } + // None => println!(" Not found"), + // } + + // section("UPDATE STUDENT"); + // reg.update(&first_id, "Victor Updated", 21, 85.0); + // println!(); + // reg.list_all(); + + section("DELETE STUDENT"); + reg.delete(&first_id); + reg.list_all(); +} diff --git a/rust_session/student_registry_v2/src/registry.rs b/rust_session/student_registry_v2/src/registry.rs new file mode 100644 index 00000000..17b33dde --- /dev/null +++ b/rust_session/student_registry_v2/src/registry.rs @@ -0,0 +1,76 @@ +use uuid::Uuid; + +use crate::grade::{Grade, Sex}; +use crate::student_struct::Student; + +pub struct Registry { + pub students: Vec, +} + +impl Registry { + pub fn new() -> Registry { + Registry { + students: Vec::new(), + } + } + + pub fn add(&mut self, name: &str, age: u8, sex: Sex, grade: Grade, score: f32) { + let id = Uuid::new_v4().to_string(); + let student = Student::new(id, name.to_string(), age, sex, grade, score); + println!(" [+] Registered: {:<16} (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!( + " {:<36} {:<16} {:<4} {:<8} {:<9} {}", + "ID", "Name", "Age", "Sex", "Grade", "Score" + ); + println!(" {}", "-".repeat(87)); + for student in &self.students { + println!( + " {:<36} {:<16} {:<4} {:<8} {:<9} {:.1}", + student.id, + student.name, + student.age, + student.sex.to_str(), + student.grade.as_str(), + student.score, + ); + } + } + + // Returns a shared reference to the student if found, or None + pub fn find_by_id(&self, id: &str) -> Option<&Student> { + self.students.iter().find(|s| s.id == id) + // ------ in javascript ------- + // this.students.find(s => s.id === id) + } + + // Updates name, age, and score for the student with the given id + pub fn update(&mut self, id: &str, name: &str, age: u8, score: f32) { + if let Some(student) = self.students.iter_mut().find(|s| s.id == id) { + student.name = name.to_string(); + student.age = age; + student.score = score; + println!(" [~] Updated: {} (ID: {})", student.name, id); + } else { + println!(" [!] Student with ID {} not found", id); + } + } + + // Removes the student with the given id from the registry + pub fn delete(&mut self, id: &str) { + let before = self.students.len(); + self.students.retain(|s| s.id != id); + if self.students.len() < before { + println!(" [-] Deleted student with ID: {}", id); + } else { + println!(" [!] Student with ID {} not found", id); + } + } +} diff --git a/rust_session/student_registry_v2/src/student_struct.rs b/rust_session/student_registry_v2/src/student_struct.rs new file mode 100644 index 00000000..3eaa29e0 --- /dev/null +++ b/rust_session/student_registry_v2/src/student_struct.rs @@ -0,0 +1,24 @@ +use crate::grade::{Grade, Sex}; + +#[derive(Debug)] +pub struct Student { + pub id: String, // UUID string instead of u32 + pub name: String, + pub age: u8, + pub sex: Sex, + pub grade: Grade, + pub score: f32, +} + +impl Student { + pub fn new(id: String, name: String, age: u8, sex: Sex, grade: Grade, score: f32) -> Student { + Student { + id, + name, + age, + sex, + grade, + score, + } + } +} From b3741dab6f779a445ccc130de70c586fb7fc3ad0 Mon Sep 17 00:00:00 2001 From: luhrhenz Date: Wed, 10 Jun 2026 15:49:20 +0100 Subject: [PATCH 2/3] chore: added update, delete and registry_v2 --- .gitignore | 4 + rust_session/Cargo.lock | 7 - rust_session/notes/borrowing.md | 31 -- rust_session/notes/overall-notes.md | 479 ----------------- rust_session/notes/ownership.md | 76 --- rust_session/notes/referencing.md | 58 -- rust_session/student_registry/Cargo.lock | 7 - .../notes/borrowing_deep_dive.md | 167 ------ .../student_registry/notes/core_logic.md | 315 ----------- .../notes/referencing_deep_dive.md | 209 -------- rust_session/student_registry/notes/struct.md | 4 - rust_session/student_registry_v2/Cargo.lock | 500 ------------------ .../student_registry_v2/notes/explanation.md | 344 ------------ 13 files changed, 4 insertions(+), 2197 deletions(-) delete mode 100644 rust_session/Cargo.lock delete mode 100644 rust_session/notes/borrowing.md delete mode 100644 rust_session/notes/overall-notes.md delete mode 100644 rust_session/notes/ownership.md delete mode 100644 rust_session/notes/referencing.md delete mode 100644 rust_session/student_registry/Cargo.lock delete mode 100644 rust_session/student_registry/notes/borrowing_deep_dive.md delete mode 100644 rust_session/student_registry/notes/core_logic.md delete mode 100644 rust_session/student_registry/notes/referencing_deep_dive.md delete mode 100644 rust_session/student_registry/notes/struct.md delete mode 100644 rust_session/student_registry_v2/Cargo.lock delete mode 100644 rust_session/student_registry_v2/notes/explanation.md diff --git a/.gitignore b/.gitignore index 04eea848..1b00319c 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,10 @@ lib/ # Rust build artifacts /target **/target +**/Cargo.lock + +# Personal notes +**/notes/ # Hardhat / Foundry build artifacts artifacts/ diff --git a/rust_session/Cargo.lock b/rust_session/Cargo.lock deleted file mode 100644 index bf268c9a..00000000 --- a/rust_session/Cargo.lock +++ /dev/null @@ -1,7 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "rust_session" -version = "0.1.0" diff --git a/rust_session/notes/borrowing.md b/rust_session/notes/borrowing.md deleted file mode 100644 index 5c4a011b..00000000 --- a/rust_session/notes/borrowing.md +++ /dev/null @@ -1,31 +0,0 @@ -## 2. Borrowing - -Constantly moving ownership in and out of functions is awkward. **Borrowing** lets you give a function access to a value *without* transferring ownership. - -You borrow a value by passing a **reference** to it. - -```rust -fn greet(s: &String) { // `s` is a reference to a String - println!("Hello, {}!", s); -} // reference ends here; nothing is dropped - -fn main() { - let name = String::from("Carol"); - greet(&name); // pass a reference — `name` keeps ownership - - println!("Still have: {}", name); // ✅ name is still valid -} -``` - -The `&` means "borrow this, don't take it". The function can use the value but cannot drop it. - -### The two kinds of borrow - -| Kind | Syntax | Can read? | Can modify? | How many at once? | -|---|---|---|---|---| -| Shared (immutable) | `&T` | ✅ | ❌ | Unlimited | -| Exclusive (mutable) | `&mut T` | ✅ | ✅ | Exactly one | - -These rules together mean: **you can never have a mutable and an immutable reference to the same value at the same time.** - ---- diff --git a/rust_session/notes/overall-notes.md b/rust_session/notes/overall-notes.md deleted file mode 100644 index 5f5d7a9f..00000000 --- a/rust_session/notes/overall-notes.md +++ /dev/null @@ -1,479 +0,0 @@ -# Rust Ownership, Borrowing & References -### A beginner's guide with annotated code - ---- - -## Why these concepts exist - -Most languages either make *you* manage memory (C, C++) or use a garbage collector to do it (Python, Java, Go). Rust takes a third path: the **compiler** enforces memory safety at compile time through three rules. No runtime cost, no GC pauses, no dangling pointers. - -The three concepts build on each other: - -``` -Ownership → who is responsible for a value -Borrowing → letting someone else use it temporarily -References → the actual mechanism for borrowing -``` - ---- - -## 1. Ownership - -Every value in Rust has exactly **one owner**. When the owner goes out of scope, the value is dropped (freed). - -```rust -fn main() { - let name = String::from("Alice"); // `name` is the owner of this String - println!("{}", name); -} // `name` goes out of scope here → String is dropped automatically -``` - -### Ownership moves on assignment - -When you assign a heap value to another variable, ownership **moves** — the original variable becomes invalid. - -```rust -fn main() { - let a = String::from("hello"); - let b = a; // ownership moves from `a` to `b` - - // println!("{}", a); // ❌ compile error: `a` was moved - println!("{}", b); // ✅ `b` is the owner now -} -``` - -> **Why?** If both `a` and `b` owned the same heap memory, Rust would try to free it twice — a classic bug. Moving prevents this. - -### Ownership moves into functions too - -```rust -fn greet(s: String) { // `s` takes ownership - println!("Hello, {}!", s); -} // `s` is dropped here - -fn main() { - let name = String::from("Bob"); - greet(name); // ownership moves into `greet` - - // println!("{}", name); // ❌ compile error: `name` was moved -} -``` - -### Copy types are different - -Primitive types like `i32`, `bool`, `f64`, and `char` implement the `Copy` trait — they are copied, not moved, because they live entirely on the stack. - -```rust -fn main() { - let x = 5; - let y = x; // `x` is copied, not moved - - println!("{} {}", x, y); // ✅ both are valid — x is still usable -} -``` - ---- - -## 2. Borrowing - -Constantly moving ownership in and out of functions is awkward. **Borrowing** lets you give a function access to a value *without* transferring ownership. - -You borrow a value by passing a **reference** to it. - -```rust -fn greet(s: &String) { // `s` is a reference to a String - println!("Hello, {}!", s); -} // reference ends here; nothing is dropped - -fn main() { - let name = String::from("Carol"); - greet(&name); // pass a reference — `name` keeps ownership - - println!("Still have: {}", name); // ✅ name is still valid -} -``` - -The `&` means "borrow this, don't take it". The function can use the value but cannot drop it. - -### The two kinds of borrow - -| Kind | Syntax | Can read? | Can modify? | How many at once? | -|---|---|---|---|---| -| Shared (immutable) | `&T` | ✅ | ❌ | Unlimited | -| Exclusive (mutable) | `&mut T` | ✅ | ✅ | Exactly one | - -These rules together mean: **you can never have a mutable and an immutable reference to the same value at the same time.** - ---- - -## 3. References - -A reference is a pointer that is *guaranteed by the compiler* to be valid. No null pointers, no dangling pointers — ever. - -### Immutable reference (`&T`) - -```rust -fn print_length(s: &String) { - println!("Length: {}", s.len()); // can read - // s.push_str(" world"); // ❌ can't modify through &String -} - -fn main() { - let message = String::from("hello"); - - let r1 = &message; // first shared borrow - let r2 = &message; // second shared borrow — totally fine - - println!("{} and {}", r1, r2); // ✅ multiple readers are safe -} -``` - -### Mutable reference (`&mut T`) - -```rust -fn append_world(s: &mut String) { - s.push_str(", world"); // ✅ can modify through &mut -} - -fn main() { - let mut message = String::from("hello"); // must be `mut` to borrow mutably - append_world(&mut message); - println!("{}", message); // "hello, world" -} -``` - -### The exclusivity rule in action - -```rust -fn main() { - let mut s = String::from("hello"); - - let r1 = &s; // immutable borrow - let r2 = &s; // another immutable borrow — fine - // let r3 = &mut s; // ❌ can't have &mut while &s exists - - println!("{} {}", r1, r2); - // r1 and r2 are no longer used after this point - - let r3 = &mut s; // ✅ now fine — r1 and r2 are done - r3.push_str("!"); - println!("{}", r3); -} -``` - -> **The rule in plain English:** You can have as many readers as you like *or* exactly one writer — never both at the same time. - ---- - -## 4. The slice — a special reference - -A **slice** is a reference to a _part_ of a collection. It borrows a window into the data without copying it. - -```rust -fn first_word(s: &str) -> &str { - let bytes = s.as_bytes(); - for (i, &byte) in bytes.iter().enumerate() { - if byte == b' ' { - return &s[0..i]; // return a slice up to the first space - } - } - &s[..] // whole string is one word -} - -fn main() { - let sentence = String::from("hello world"); - let word = first_word(&sentence); - println!("First word: {}", word); // "hello" -} -``` - ---- - -## 5. Putting it all together - -```rust -struct Player { - name: String, - score: u32, -} - -// Takes ownership of `player` — caller loses it -fn destroy(player: Player) { - println!("{} removed.", player.name); -} - -// Borrows immutably — caller keeps ownership, can't modify -fn describe(player: &Player) { - println!("{} has {} points.", player.name, player.score); -} - -// Borrows mutably — caller keeps ownership, function can modify -fn award_points(player: &mut Player, points: u32) { - player.score += points; -} - -fn main() { - let mut p = Player { - name: String::from("Dave"), - score: 0, - }; - - award_points(&mut p, 10); // mutably borrow - describe(&p); // immutably borrow — "Dave has 10 points." - award_points(&mut p, 5); // mutably borrow again - describe(&p); // "Dave has 15 points." - - destroy(p); // move ownership in — `p` is invalid after this - // describe(&p); // ❌ compile error: `p` was moved -} -``` - ---- - -## 6. Stack frames - -Think of the stack like a stack of trays in a cafeteria. Every time you call a function, a new tray is placed on top. Every local variable in that function lives on that tray. When the function returns, the tray is removed and everything on it disappears instantly. - -```rust -fn add(a: i32, b: i32) -> i32 { - // When `add` is called, a NEW frame is pushed onto the stack. - // `a`, `b`, and `result` all live inside this frame. - let result = a + b; - result - // Frame is popped here — `a`, `b`, `result` are gone. -} - -fn main() { - // main() has its own frame on the stack. - let x = 10; // lives in main's frame - let y = 20; // lives in main's frame - - let z = add(x, y); // a new frame for `add` is pushed on top of main's frame - // when add() returns, its frame is popped - // main's frame (x, y, z) is still there - - println!("{} + {} = {}", x, y, z); // 10 + 20 = 30 -} // main's frame is popped — program ends -``` - -### Each function call gets its own frame — even recursive ones - -```rust -fn countdown(n: i32) { - // Each call to `countdown` gets its OWN frame with its OWN copy of `n`. - // They do not share `n` — each frame is independent. - if n == 0 { - println!("Go!"); - return; // this frame is popped - } - println!("{}...", n); - countdown(n - 1); // a new frame is pushed on top, with n-1 - // when it returns, we come back to THIS frame -} // this frame is popped - -fn main() { - countdown(3); - // Stack during execution (top = most recent): - // countdown(0) ← top - // countdown(1) - // countdown(2) - // countdown(3) - // main ← bottom -} -``` - -> **Key rule:** the stack frame only exists while the function is running. You cannot return a reference to a local variable — the frame (and the variable) will be gone. - -```rust -// ❌ This does NOT compile — and that's a good thing. -fn make_number() -> &i32 { - let n = 42; - &n // can't return a reference to `n` — `n` dies when this frame pops -} - -// ✅ Return the value itself instead (copy it out of the frame). -fn make_number() -> i32 { - let n = 42; - n // value is copied into the caller's frame -} -``` - ---- - -## 7. The memory allocator - -When you need memory whose size isn't known until runtime — like a `String` the user types in, or a `Vec` that grows — the **allocator** steps in. It manages a large region of memory called the **heap** and hands out chunks on demand. - -In Rust you rarely talk to the allocator directly. Types like `String`, `Vec`, and `Box` do it for you. - -```rust -fn main() { - // String::from calls the allocator: "give me enough heap space for 5 bytes" - let s = String::from("hello"); - // s on the stack looks like: [ ptr | len=5 | cap=5 ] - // | - // └──► [ h | e | l | l | o ] ← heap - println!("{}", s); -} // s goes out of scope → Rust calls the allocator: "take this memory back" - // No manual free(), no garbage collector needed. -``` - -### The allocator grows memory when needed - -```rust -fn main() { - let mut v: Vec = Vec::new(); // allocator gives a small block on the heap - println!("len={}, cap={}", v.len(), v.capacity()); // len=0, cap=0 - - v.push(1); // not enough space → allocator gives a bigger block, data is moved - v.push(2); - v.push(3); - println!("len={}, cap={}", v.len(), v.capacity()); // len=3, cap=4 (typical) - - v.push(4); - v.push(5); // exceeded cap=4 → allocator gives an even bigger block again - println!("len={}, cap={}", v.len(), v.capacity()); // len=5, cap=8 (typical) -} // allocator reclaims all heap memory here -``` - -### `Box` — putting a single value on the heap explicitly - -```rust -fn main() { - let x: i32 = 7; // 7 lives on the stack - let b: Box = Box::new(7); // 7 lives on the heap; `b` is a pointer on the stack - - println!("stack x = {}", x); - println!("heap b = {}", b); // Box auto-derefs, prints 7 - - // When `b` goes out of scope, Box calls the allocator to free that heap slot. - // You never need to call free() yourself. -} -``` - -> **One sentence summary:** the stack is fast but temporary; the heap is flexible but requires the allocator to track what's in use. Rust's ownership rules ensure the allocator is always called exactly once to free each allocation. - ---- - -## 8. Pointers and addresses - -Every byte in your computer's memory has a numbered address, like a house number on a street. A **pointer** is simply a variable that stores one of those addresses — it *points to* where some data lives. - -In Rust, references (`&T`) *are* pointers under the hood. The compiler just adds safety rules on top. - -```rust -fn main() { - let x: i32 = 42; - - // `addr` is a raw pointer to x's address in memory. - // We use `&raw const` to get it without creating a safe reference. - let addr = &raw const x as usize; // cast address to a plain number - - println!("value of x : {}", x); - println!("address of x: 0x{:x}", addr); // e.g. 0x7ffee3b2c4bc (will vary each run) -} -``` - -The address printed will be different every time — the OS places your program at a different spot in memory on each run. What matters is the *concept*: `x` is data sitting at some address, and a pointer holds that address. - -### A reference IS a pointer — with compiler guarantees - -```rust -fn main() { - let x: i32 = 100; - let r: &i32 = &x; // `r` is a reference (a safe pointer) to `x` - - // Print the value `r` points to - println!("value : {}", *r); // dereference with `*` to get the value → 100 - - // Print the address `r` holds - println!("address: {:p}", r); // {:p} formats a reference as an address - // e.g. 0x7ffee3b2c490 -} -``` - -`{:p}` is the "pointer" format — it prints the memory address rather than the value. - -### Following the pointer — dereferencing - -Dereferencing (`*`) means "go to the address this pointer holds and give me what's there". - -```rust -fn main() { - let mut score: i32 = 50; - let r: &mut i32 = &mut score; // mutable reference — a pointer that allows writing - - println!("before: {}", score); // 50 - - *r = 99; // dereference + assign: go to the address, write 99 there - - println!("after : {}", score); // 99 ← `score` changed because `r` pointed at it -} -``` - -### Pointers to heap data — what `Box` looks like - -```rust -fn main() { - let b: Box = Box::new(7); - // b on the stack: [ 0x... ] ← an address - // | - // └──► [ 7 ] ← heap, at that address - - println!("b points to address : {:p}", b); // address on the heap - println!("value at that address: {}", *b); // 7 - - // Box is 8 bytes on a 64-bit system (just the address). - // The i32 itself is 4 bytes, living on the heap at that address. - println!("size of Box on stack: {} bytes", std::mem::size_of::>()); // 8 - println!("size of i32 on heap : {} bytes", std::mem::size_of::()); // 4 -} -``` - -### Visualising the relationship - -``` -Stack Heap -───────────────────── ────────────────── -b │ 0x55a3f1c0 │──────────► │ 7 │ (4 bytes at 0x55a3f1c0) - └────────────┘ └─────┘ - (8 bytes — just an address) -``` - -> **One sentence summary:** a pointer is just a number — a memory address. A Rust reference is a pointer that the compiler has verified is always valid, always properly aligned, and never aliased unsafely. - ---- - -## Quick-reference summary - -``` -OWNERSHIP - let x = value; x owns value - let y = x; ownership moves to y (x invalid for heap types) - let y = x.clone(); deep copy — both valid - -BORROWING - fn f(x: &T) immutable borrow — read only - fn f(x: &mut T) mutable borrow — read + write - -REFERENCES - &value create an immutable reference - &mut value create a mutable reference - Rules enforced at compile time: - - unlimited & at the same time - - only one &mut, and never alongside & - -SLICES - &s[0..n] reference to part of a string or vec -``` - ---- - -## Common beginner errors and fixes - -| Error message | What happened | Fix | -| ------------------------------------------------------------- | ------------------------------------------ | ----------------------------------------------------------------- | -| `value used after move` | You moved a value then tried to use it | Use `&` to borrow, or `.clone()` to copy | -| `cannot borrow as mutable` | You forgot `mut` on the variable | Change `let x` to `let mut x` | -| `cannot borrow as mutable because also borrowed as immutable` | You have `&` and `&mut` at the same time | End the immutable borrow before creating the mutable one | -| `does not live long enough` | A reference outlives the data it points to | Make sure the owned value lives at least as long as the reference | diff --git a/rust_session/notes/ownership.md b/rust_session/notes/ownership.md deleted file mode 100644 index 63c16e48..00000000 --- a/rust_session/notes/ownership.md +++ /dev/null @@ -1,76 +0,0 @@ -# Rust Ownership, Borrowing & References - -### A beginner's guide with annotated code - ---- - -## Why these concepts exist - -Most languages either make _you_ manage memory (C, C++) or use a garbage collector to do it (Python, Java, Go). Rust takes a third path: the **compiler** enforces memory safety at compile time through three rules. No runtime cost, no GC pauses, no dangling pointers. - -The three concepts build on each other: - -``` -Ownership → who is responsible for a value -Borrowing → letting someone else use it temporarily -References → the actual mechanism for borrowing -``` - ---- - -## 1. Ownership - -Every value in Rust has exactly **one owner**. When the owner goes out of scope, the value is dropped (freed). - -```rust -fn main() { - let name = String::from("Alice"); // `name` is the owner of this String - println!("{}", name); -} // `name` goes out of scope here → String is dropped automatically -``` - -### Ownership moves on assignment - -When you assign a heap value to another variable, ownership **moves** — the original variable becomes invalid. - -```rust -fn main() { - let a = String::from("hello"); - let b = a; // ownership moves from `a` to `b` - - // println!("{}", a); // ❌ compile error: `a` was moved - println!("{}", b); // ✅ `b` is the owner now -} -``` - -> **Why?** If both `a` and `b` owned the same heap memory, Rust would try to free it twice — a classic bug. Moving prevents this. - -### Ownership moves into functions too - -```rust -fn greet(s: String) { // `s` takes ownership - println!("Hello, {}!", s); -} // `s` is dropped here - -fn main() { - let name = String::from("Bob"); - greet(name); // ownership moves into `greet` - - // println!("{}", name); // ❌ compile error: `name` was moved -} -``` - -### Copy types are different - -Primitive types like `i32`, `bool`, `f64`, and `char` implement the `Copy` trait — they are copied, not moved, because they live entirely on the stack. - -```rust -fn main() { - let x = 5; - let y = x; // `x` is copied, not moved - - println!("{} {}", x, y); // ✅ both are valid — x is still usable -} -``` - ---- diff --git a/rust_session/notes/referencing.md b/rust_session/notes/referencing.md deleted file mode 100644 index 3f4f267c..00000000 --- a/rust_session/notes/referencing.md +++ /dev/null @@ -1,58 +0,0 @@ -## 3. References - -A reference is a pointer that is _guaranteed by the compiler_ to be valid. No null pointers, no dangling pointers — ever. - -### Immutable reference (`&T`) - -```rust -fn print_length(s: &String) { - println!("Length: {}", s.len()); // can read - // s.push_str(" world"); // ❌ can't modify through &String -} - -fn main() { - let message = String::from("hello"); - - let r1 = &message; // first shared borrow - let r2 = &message; // second shared borrow — totally fine - - println!("{} and {}", r1, r2); // ✅ multiple readers are safe -} -``` - -### Mutable reference (`&mut T`) - -```rust -fn append_world(s: &mut String) { - s.push_str(", world"); // ✅ can modify through &mut -} - -fn main() { - let mut message = String::from("hello"); // must be `mut` to borrow mutably - append_world(&mut message); - println!("{}", message); // "hello, world" -} -``` - -### The exclusivity rule in action - -```rust -fn main() { - let mut s = String::from("hello"); - - let r1 = &s; // immutable borrow - let r2 = &s; // another immutable borrow — fine - // let r3 = &mut s; // ❌ can't have &mut while &s exists - - println!("{} {}", r1, r2); - // r1 and r2 are no longer used after this point - - let r3 = &mut s; // ✅ now fine — r1 and r2 are done - r3.push_str("!"); - println!("{}", r3); -} -``` - -> **The rule in plain English:** You can have as many readers as you like _or_ exactly one writer — never both at the same time. - ---- diff --git a/rust_session/student_registry/Cargo.lock b/rust_session/student_registry/Cargo.lock deleted file mode 100644 index e7f8ee38..00000000 --- a/rust_session/student_registry/Cargo.lock +++ /dev/null @@ -1,7 +0,0 @@ -# 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/student_registry/notes/borrowing_deep_dive.md b/rust_session/student_registry/notes/borrowing_deep_dive.md deleted file mode 100644 index 76411372..00000000 --- a/rust_session/student_registry/notes/borrowing_deep_dive.md +++ /dev/null @@ -1,167 +0,0 @@ -# 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/student_registry/notes/core_logic.md b/rust_session/student_registry/notes/core_logic.md deleted file mode 100644 index 5383ef4e..00000000 --- a/rust_session/student_registry/notes/core_logic.md +++ /dev/null @@ -1,315 +0,0 @@ -# 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/student_registry/notes/referencing_deep_dive.md b/rust_session/student_registry/notes/referencing_deep_dive.md deleted file mode 100644 index c422de29..00000000 --- a/rust_session/student_registry/notes/referencing_deep_dive.md +++ /dev/null @@ -1,209 +0,0 @@ -# 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/student_registry/notes/struct.md b/rust_session/student_registry/notes/struct.md deleted file mode 100644 index 162fa495..00000000 --- a/rust_session/student_registry/notes/struct.md +++ /dev/null @@ -1,4 +0,0 @@ -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/student_registry_v2/Cargo.lock b/rust_session/student_registry_v2/Cargo.lock deleted file mode 100644 index a4be051f..00000000 --- a/rust_session/student_registry_v2/Cargo.lock +++ /dev/null @@ -1,500 +0,0 @@ -# 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_v2" -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/student_registry_v2/notes/explanation.md b/rust_session/student_registry_v2/notes/explanation.md deleted file mode 100644 index c66d38cd..00000000 --- a/rust_session/student_registry_v2/notes/explanation.md +++ /dev/null @@ -1,344 +0,0 @@ -# Student Registry V2 — Code Explanation - -## What is this project? - -This is a simple in-memory student registry built in Rust. -"In-memory" means the data only lives while the program is running — nothing is saved to a file or database. - -The big difference from v1 is that instead of giving each student a simple number as their ID (`1`, `2`, `3`...), -we use a **UUID** — a randomly generated unique string like `f3dbcfa8-a037-4a8e-82d8-21b4b6235c2d`. - ---- - -## Project Structure - -``` -student_registry_v2/ -├── Cargo.toml ← project config + dependencies -└── src/ - ├── main.rs ← entry point, where execution starts - ├── grade.rs ← Grade and Sex enums - ├── student_struct.rs ← the Student data type - └── registry.rs ← the Registry that manages students -``` - ---- - -## File by File - ---- - -### `Cargo.toml` - -```toml -[dependencies] -uuid = { version = "1", features = ["v4"] } -``` - -This tells Cargo (Rust's package manager) to download and include the `uuid` library. -The `features = ["v4"]` part means we want version 4 UUIDs — the kind that are **randomly generated**. - ---- - -### `grade.rs` — Enums - -```rust -pub enum Grade { - First, - Second, - Third, -} -``` - -An **enum** is a type that can only be one of a fixed set of values. -Think of it like a multiple-choice answer — a student's grade can only be First, Second, or Third, nothing else. - -```rust -impl Grade { - pub fn as_str(&self) -> &str { - match self { - Grade::First => "Cohort 1", - Grade::Second => "Cohort 2", - Grade::Third => "Cohort 3", - } - } -} -``` - -`impl Grade` adds methods to the enum — just like adding functions that belong to it. -`as_str()` takes the current value (`&self`) and uses `match` to return its text label. -`match` is Rust's version of a switch statement, but it must cover every possible case. - -`&str` is a borrowed string slice — a reference to text. It doesn't own the string, just points to it. - -```rust -pub enum Sex { - Male, - Female, -} -``` - -Same idea — Sex can only be Male or Female. - -```rust -impl Sex { - pub fn to_str(&self) -> &str { - match self { - Sex::Male => "male", - Sex::Female => "female", - } - } -} -``` - ---- - -### `student_struct.rs` — The Student - -```rust -#[derive(Debug)] -pub struct Student { - pub id: String, - pub name: String, - pub age: u8, - pub sex: Sex, - pub grade: Grade, - pub score: f32, -} -``` - -A **struct** is a custom data type that groups related fields together. -Think of it like a form — every student has the same fields, but different values. - -| Field | Type | Meaning | -|---------|----------|----------------------------------------------| -| `id` | `String` | UUID — unique identifier (random string) | -| `name` | `String` | Heap-allocated text, owned by this struct | -| `age` | `u8` | Unsigned 8-bit integer (0–255), enough for age | -| `sex` | `Sex` | Our own enum from grade.rs | -| `grade` | `Grade` | Our own enum from grade.rs | -| `score` | `f32` | 32-bit floating point number (e.g. 78.5) | - -`#[derive(Debug)]` automatically gives the struct the ability to be printed with `{:?}` or `{:#?}`. - -`pub` on each field means code outside this file can read and write those fields directly. - -```rust -impl Student { - pub fn new(id: String, name: String, age: u8, sex: Sex, grade: Grade, score: f32) -> Student { - Student { id, name, age, sex, grade, score } - } -} -``` - -`new()` is a constructor — a function that builds and returns a `Student`. -Rust has no built-in constructor keyword, so by convention we write a `new()` function. -`-> Student` means this function returns a `Student` value. - ---- - -### `registry.rs` — The Registry (the core logic) - -```rust -pub struct Registry { - pub students: Vec, -} -``` - -`Vec` is a growable list of students — like an array that can expand. -In v1 there was also a `next_id: u32` counter here. In v2 it's gone — UUID handles its own uniqueness. - ---- - -#### `new()` -```rust -pub fn new() -> Registry { - Registry { - students: Vec::new(), - } -} -``` -Creates an empty registry with an empty list. `Vec::new()` gives you an empty vector. - ---- - -#### `add()` -```rust -pub fn add(&mut self, name: &str, age: u8, sex: Sex, grade: Grade, score: f32) { - let id = Uuid::new_v4().to_string(); - let student = Student::new(id, name.to_string(), age, sex, grade, score); - println!("Added: {} (ID: {})", student.name, student.id); - self.students.push(student); -} -``` - -- `&mut self` — the registry is borrowed mutably, meaning we're allowed to change it -- `Uuid::new_v4()` — generates a random UUID (128-bit number) -- `.to_string()` — converts it into a regular `String` -- `name.to_string()` — `name` comes in as `&str` (a reference), we convert it to an owned `String` so the student can own its name -- `.push(student)` — appends the student to the end of the vector - ---- - -#### `list_all()` -```rust -pub fn list_all(&self) { - if self.students.is_empty() { ... } - for student in &self.students { ... } -} -``` - -- `&self` — borrowed immutably, we're only reading, not changing -- `&self.students` — we iterate over references to the students (we don't consume or move them) -- `for student in &self.students` — `student` here is `&Student`, a reference to each one - ---- - -#### `find_by_id()` -```rust -pub fn find_by_id(&self, id: &str) -> Option<&Student> { - self.students.iter().find(|s| s.id == id) -} -``` - -- `.iter()` — creates an iterator over references to the students -- `.find(|s| s.id == id)` — goes through each student and returns the first one where `s.id == id` -- `Option<&Student>` — the return type. In Rust there is no `null`. Instead: - - `Some(&student)` is returned if a match is found - - `None` is returned if nothing matches -- This forces you to handle both cases, preventing null pointer crashes - ---- - -#### `update()` -```rust -pub fn update(&mut self, id: &str, name: &str, age: u8, score: f32) { - if let Some(student) = self.students.iter_mut().find(|s| s.id == id) { - student.name = name.to_string(); - student.age = age; - student.score = score; - println!("Updated student with ID: {}", id); - } else { - println!("Student with ID {} not found", id); - } -} -``` - -- `.iter_mut()` — like `.iter()` but gives **mutable** references, so we can modify fields -- `if let Some(student)` — this is Rust's clean way of saying "if the Option has a value, unwrap it into `student` and run this block, otherwise go to `else`" -- We then directly reassign the fields on the found student - ---- - -#### `delete()` -```rust -pub fn delete(&mut self, id: &str) { - let before = self.students.len(); - self.students.retain(|s| s.id != id); - if self.students.len() < before { - println!("Deleted student with ID: {}", id); - } else { - println!("Student with ID {} not found", id); - } -} -``` - -- `.retain(|s| s.id != id)` — keeps only the students where the condition is `true` - - Students whose id does NOT match the given id are kept - - The one whose id matches is silently dropped -- We compare `len()` before and after to know if anything was actually removed - ---- - -### `main.rs` — Putting it all together - -```rust -mod grade; -mod registry; -mod student_struct; -``` - -`mod` tells Rust to include these files as modules. Without this, the files are invisible to the compiler. - -```rust -use grade::{Grade, Sex}; -use registry::Registry; -``` - -`use` brings names into scope so you don't have to write `grade::Grade` every time. - -```rust -let mut reg = Registry::new(); -``` - -`let` declares a variable. `mut` means it's mutable (changeable). Without `mut`, Rust won't let you call methods that modify it. - -```rust -reg.add("Victor", 20, Sex::Male, Grade::First, 78.5); -``` - -Adds Victor to the registry. Inside `add()`, a UUID is generated automatically for him. - -```rust -let first_id = reg.students[0].id.clone(); -``` - -Gets the id of the first student. `.clone()` is needed because `id` is a `String` — if we just wrote `reg.students[0].id`, we'd be moving it out of the struct (Rust won't allow that while the struct is still alive). `.clone()` makes a copy instead. - -```rust -match reg.find_by_id(&first_id) { - Some(s) => println!("Found: {:#?}", s), - None => println!("Not found"), -} -``` - -`match` on an `Option` is the standard Rust pattern. We handle both outcomes: -- `Some(s)` — found, print the student with pretty debug formatting (`{:#?}`) -- `None` — not found, print a message - -```rust -reg.update(&first_id, "Victor Updated", 21, 85.0); -``` - -Updates Victor's name, age, and score. The id stays the same — we're just changing the other fields. - -```rust -reg.delete(&first_id); -``` - -Removes Victor from the registry entirely. - ---- - -## v1 vs v2 — The key differences - -| | v1 | v2 | -|---|---|---| -| Student ID type | `u32` (number) | `String` (UUID) | -| ID generation | `next_id` counter in Registry | `Uuid::new_v4()` | -| Predictable IDs | yes (0, 1, 2...) | no (random each run) | -| Works across systems | no | yes | -| find_by_id | not implemented | `Option<&Student>` | -| update | not implemented | `.iter_mut()` + field assignment | -| delete | not implemented | `.retain()` | -| External dependency | none | `uuid` crate | - ---- - -## Key Rust Concepts Used - -| Concept | Where | What it means | -|---|---|---| -| `struct` | `Student`, `Registry` | Custom data type grouping fields | -| `enum` | `Grade`, `Sex` | A type with a fixed set of variants | -| `impl` | all structs/enums | Adds methods to a type | -| `Vec` | `students` field | A growable list | -| `Option` | `find_by_id` return | Either `Some(value)` or `None` — no nulls | -| `&self` | read-only methods | Borrow the value without taking ownership | -| `&mut self` | mutating methods | Borrow the value and allow changes | -| `.iter()` | `list_all`, `find_by_id` | Loop without consuming the collection | -| `.iter_mut()` | `update` | Loop with ability to modify elements | -| `.retain()` | `delete` | Keep only elements matching a condition | -| `.clone()` | `main.rs` | Make a deep copy of an owned value | -| `if let Some(x)` | `update` | Unwrap an Option only if it has a value | -| `match` | everywhere | Pattern match — must handle all cases | From 55c613e21f762e7a4cc09181b09159ad3ddc2cb6 Mon Sep 17 00:00:00 2001 From: luhrhenz Date: Wed, 10 Jun 2026 15:54:49 +0100 Subject: [PATCH 3/3] chore: added update, delete and registry_v2 --- .gitignore | 2 +- rust_session/notes/borrowing.md | 31 ++ rust_session/notes/overall-notes.md | 479 ++++++++++++++++++ rust_session/notes/ownership.md | 76 +++ rust_session/notes/referencing.md | 58 +++ .../notes/borrowing_deep_dive.md | 167 ++++++ .../student_registry/notes/core_logic.md | 315 ++++++++++++ .../notes/referencing_deep_dive.md | 209 ++++++++ rust_session/student_registry/notes/struct.md | 4 + 9 files changed, 1340 insertions(+), 1 deletion(-) create mode 100644 rust_session/notes/borrowing.md create mode 100644 rust_session/notes/overall-notes.md create mode 100644 rust_session/notes/ownership.md create mode 100644 rust_session/notes/referencing.md create mode 100644 rust_session/student_registry/notes/borrowing_deep_dive.md create mode 100644 rust_session/student_registry/notes/core_logic.md create mode 100644 rust_session/student_registry/notes/referencing_deep_dive.md create mode 100644 rust_session/student_registry/notes/struct.md diff --git a/.gitignore b/.gitignore index 1b00319c..9e078da1 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,7 @@ lib/ **/Cargo.lock # Personal notes -**/notes/ +rust_session/student_registry_v2/notes/ # Hardhat / Foundry build artifacts artifacts/ diff --git a/rust_session/notes/borrowing.md b/rust_session/notes/borrowing.md new file mode 100644 index 00000000..5c4a011b --- /dev/null +++ b/rust_session/notes/borrowing.md @@ -0,0 +1,31 @@ +## 2. Borrowing + +Constantly moving ownership in and out of functions is awkward. **Borrowing** lets you give a function access to a value *without* transferring ownership. + +You borrow a value by passing a **reference** to it. + +```rust +fn greet(s: &String) { // `s` is a reference to a String + println!("Hello, {}!", s); +} // reference ends here; nothing is dropped + +fn main() { + let name = String::from("Carol"); + greet(&name); // pass a reference — `name` keeps ownership + + println!("Still have: {}", name); // ✅ name is still valid +} +``` + +The `&` means "borrow this, don't take it". The function can use the value but cannot drop it. + +### The two kinds of borrow + +| Kind | Syntax | Can read? | Can modify? | How many at once? | +|---|---|---|---|---| +| Shared (immutable) | `&T` | ✅ | ❌ | Unlimited | +| Exclusive (mutable) | `&mut T` | ✅ | ✅ | Exactly one | + +These rules together mean: **you can never have a mutable and an immutable reference to the same value at the same time.** + +--- diff --git a/rust_session/notes/overall-notes.md b/rust_session/notes/overall-notes.md new file mode 100644 index 00000000..5f5d7a9f --- /dev/null +++ b/rust_session/notes/overall-notes.md @@ -0,0 +1,479 @@ +# Rust Ownership, Borrowing & References +### A beginner's guide with annotated code + +--- + +## Why these concepts exist + +Most languages either make *you* manage memory (C, C++) or use a garbage collector to do it (Python, Java, Go). Rust takes a third path: the **compiler** enforces memory safety at compile time through three rules. No runtime cost, no GC pauses, no dangling pointers. + +The three concepts build on each other: + +``` +Ownership → who is responsible for a value +Borrowing → letting someone else use it temporarily +References → the actual mechanism for borrowing +``` + +--- + +## 1. Ownership + +Every value in Rust has exactly **one owner**. When the owner goes out of scope, the value is dropped (freed). + +```rust +fn main() { + let name = String::from("Alice"); // `name` is the owner of this String + println!("{}", name); +} // `name` goes out of scope here → String is dropped automatically +``` + +### Ownership moves on assignment + +When you assign a heap value to another variable, ownership **moves** — the original variable becomes invalid. + +```rust +fn main() { + let a = String::from("hello"); + let b = a; // ownership moves from `a` to `b` + + // println!("{}", a); // ❌ compile error: `a` was moved + println!("{}", b); // ✅ `b` is the owner now +} +``` + +> **Why?** If both `a` and `b` owned the same heap memory, Rust would try to free it twice — a classic bug. Moving prevents this. + +### Ownership moves into functions too + +```rust +fn greet(s: String) { // `s` takes ownership + println!("Hello, {}!", s); +} // `s` is dropped here + +fn main() { + let name = String::from("Bob"); + greet(name); // ownership moves into `greet` + + // println!("{}", name); // ❌ compile error: `name` was moved +} +``` + +### Copy types are different + +Primitive types like `i32`, `bool`, `f64`, and `char` implement the `Copy` trait — they are copied, not moved, because they live entirely on the stack. + +```rust +fn main() { + let x = 5; + let y = x; // `x` is copied, not moved + + println!("{} {}", x, y); // ✅ both are valid — x is still usable +} +``` + +--- + +## 2. Borrowing + +Constantly moving ownership in and out of functions is awkward. **Borrowing** lets you give a function access to a value *without* transferring ownership. + +You borrow a value by passing a **reference** to it. + +```rust +fn greet(s: &String) { // `s` is a reference to a String + println!("Hello, {}!", s); +} // reference ends here; nothing is dropped + +fn main() { + let name = String::from("Carol"); + greet(&name); // pass a reference — `name` keeps ownership + + println!("Still have: {}", name); // ✅ name is still valid +} +``` + +The `&` means "borrow this, don't take it". The function can use the value but cannot drop it. + +### The two kinds of borrow + +| Kind | Syntax | Can read? | Can modify? | How many at once? | +|---|---|---|---|---| +| Shared (immutable) | `&T` | ✅ | ❌ | Unlimited | +| Exclusive (mutable) | `&mut T` | ✅ | ✅ | Exactly one | + +These rules together mean: **you can never have a mutable and an immutable reference to the same value at the same time.** + +--- + +## 3. References + +A reference is a pointer that is *guaranteed by the compiler* to be valid. No null pointers, no dangling pointers — ever. + +### Immutable reference (`&T`) + +```rust +fn print_length(s: &String) { + println!("Length: {}", s.len()); // can read + // s.push_str(" world"); // ❌ can't modify through &String +} + +fn main() { + let message = String::from("hello"); + + let r1 = &message; // first shared borrow + let r2 = &message; // second shared borrow — totally fine + + println!("{} and {}", r1, r2); // ✅ multiple readers are safe +} +``` + +### Mutable reference (`&mut T`) + +```rust +fn append_world(s: &mut String) { + s.push_str(", world"); // ✅ can modify through &mut +} + +fn main() { + let mut message = String::from("hello"); // must be `mut` to borrow mutably + append_world(&mut message); + println!("{}", message); // "hello, world" +} +``` + +### The exclusivity rule in action + +```rust +fn main() { + let mut s = String::from("hello"); + + let r1 = &s; // immutable borrow + let r2 = &s; // another immutable borrow — fine + // let r3 = &mut s; // ❌ can't have &mut while &s exists + + println!("{} {}", r1, r2); + // r1 and r2 are no longer used after this point + + let r3 = &mut s; // ✅ now fine — r1 and r2 are done + r3.push_str("!"); + println!("{}", r3); +} +``` + +> **The rule in plain English:** You can have as many readers as you like *or* exactly one writer — never both at the same time. + +--- + +## 4. The slice — a special reference + +A **slice** is a reference to a _part_ of a collection. It borrows a window into the data without copying it. + +```rust +fn first_word(s: &str) -> &str { + let bytes = s.as_bytes(); + for (i, &byte) in bytes.iter().enumerate() { + if byte == b' ' { + return &s[0..i]; // return a slice up to the first space + } + } + &s[..] // whole string is one word +} + +fn main() { + let sentence = String::from("hello world"); + let word = first_word(&sentence); + println!("First word: {}", word); // "hello" +} +``` + +--- + +## 5. Putting it all together + +```rust +struct Player { + name: String, + score: u32, +} + +// Takes ownership of `player` — caller loses it +fn destroy(player: Player) { + println!("{} removed.", player.name); +} + +// Borrows immutably — caller keeps ownership, can't modify +fn describe(player: &Player) { + println!("{} has {} points.", player.name, player.score); +} + +// Borrows mutably — caller keeps ownership, function can modify +fn award_points(player: &mut Player, points: u32) { + player.score += points; +} + +fn main() { + let mut p = Player { + name: String::from("Dave"), + score: 0, + }; + + award_points(&mut p, 10); // mutably borrow + describe(&p); // immutably borrow — "Dave has 10 points." + award_points(&mut p, 5); // mutably borrow again + describe(&p); // "Dave has 15 points." + + destroy(p); // move ownership in — `p` is invalid after this + // describe(&p); // ❌ compile error: `p` was moved +} +``` + +--- + +## 6. Stack frames + +Think of the stack like a stack of trays in a cafeteria. Every time you call a function, a new tray is placed on top. Every local variable in that function lives on that tray. When the function returns, the tray is removed and everything on it disappears instantly. + +```rust +fn add(a: i32, b: i32) -> i32 { + // When `add` is called, a NEW frame is pushed onto the stack. + // `a`, `b`, and `result` all live inside this frame. + let result = a + b; + result + // Frame is popped here — `a`, `b`, `result` are gone. +} + +fn main() { + // main() has its own frame on the stack. + let x = 10; // lives in main's frame + let y = 20; // lives in main's frame + + let z = add(x, y); // a new frame for `add` is pushed on top of main's frame + // when add() returns, its frame is popped + // main's frame (x, y, z) is still there + + println!("{} + {} = {}", x, y, z); // 10 + 20 = 30 +} // main's frame is popped — program ends +``` + +### Each function call gets its own frame — even recursive ones + +```rust +fn countdown(n: i32) { + // Each call to `countdown` gets its OWN frame with its OWN copy of `n`. + // They do not share `n` — each frame is independent. + if n == 0 { + println!("Go!"); + return; // this frame is popped + } + println!("{}...", n); + countdown(n - 1); // a new frame is pushed on top, with n-1 + // when it returns, we come back to THIS frame +} // this frame is popped + +fn main() { + countdown(3); + // Stack during execution (top = most recent): + // countdown(0) ← top + // countdown(1) + // countdown(2) + // countdown(3) + // main ← bottom +} +``` + +> **Key rule:** the stack frame only exists while the function is running. You cannot return a reference to a local variable — the frame (and the variable) will be gone. + +```rust +// ❌ This does NOT compile — and that's a good thing. +fn make_number() -> &i32 { + let n = 42; + &n // can't return a reference to `n` — `n` dies when this frame pops +} + +// ✅ Return the value itself instead (copy it out of the frame). +fn make_number() -> i32 { + let n = 42; + n // value is copied into the caller's frame +} +``` + +--- + +## 7. The memory allocator + +When you need memory whose size isn't known until runtime — like a `String` the user types in, or a `Vec` that grows — the **allocator** steps in. It manages a large region of memory called the **heap** and hands out chunks on demand. + +In Rust you rarely talk to the allocator directly. Types like `String`, `Vec`, and `Box` do it for you. + +```rust +fn main() { + // String::from calls the allocator: "give me enough heap space for 5 bytes" + let s = String::from("hello"); + // s on the stack looks like: [ ptr | len=5 | cap=5 ] + // | + // └──► [ h | e | l | l | o ] ← heap + println!("{}", s); +} // s goes out of scope → Rust calls the allocator: "take this memory back" + // No manual free(), no garbage collector needed. +``` + +### The allocator grows memory when needed + +```rust +fn main() { + let mut v: Vec = Vec::new(); // allocator gives a small block on the heap + println!("len={}, cap={}", v.len(), v.capacity()); // len=0, cap=0 + + v.push(1); // not enough space → allocator gives a bigger block, data is moved + v.push(2); + v.push(3); + println!("len={}, cap={}", v.len(), v.capacity()); // len=3, cap=4 (typical) + + v.push(4); + v.push(5); // exceeded cap=4 → allocator gives an even bigger block again + println!("len={}, cap={}", v.len(), v.capacity()); // len=5, cap=8 (typical) +} // allocator reclaims all heap memory here +``` + +### `Box` — putting a single value on the heap explicitly + +```rust +fn main() { + let x: i32 = 7; // 7 lives on the stack + let b: Box = Box::new(7); // 7 lives on the heap; `b` is a pointer on the stack + + println!("stack x = {}", x); + println!("heap b = {}", b); // Box auto-derefs, prints 7 + + // When `b` goes out of scope, Box calls the allocator to free that heap slot. + // You never need to call free() yourself. +} +``` + +> **One sentence summary:** the stack is fast but temporary; the heap is flexible but requires the allocator to track what's in use. Rust's ownership rules ensure the allocator is always called exactly once to free each allocation. + +--- + +## 8. Pointers and addresses + +Every byte in your computer's memory has a numbered address, like a house number on a street. A **pointer** is simply a variable that stores one of those addresses — it *points to* where some data lives. + +In Rust, references (`&T`) *are* pointers under the hood. The compiler just adds safety rules on top. + +```rust +fn main() { + let x: i32 = 42; + + // `addr` is a raw pointer to x's address in memory. + // We use `&raw const` to get it without creating a safe reference. + let addr = &raw const x as usize; // cast address to a plain number + + println!("value of x : {}", x); + println!("address of x: 0x{:x}", addr); // e.g. 0x7ffee3b2c4bc (will vary each run) +} +``` + +The address printed will be different every time — the OS places your program at a different spot in memory on each run. What matters is the *concept*: `x` is data sitting at some address, and a pointer holds that address. + +### A reference IS a pointer — with compiler guarantees + +```rust +fn main() { + let x: i32 = 100; + let r: &i32 = &x; // `r` is a reference (a safe pointer) to `x` + + // Print the value `r` points to + println!("value : {}", *r); // dereference with `*` to get the value → 100 + + // Print the address `r` holds + println!("address: {:p}", r); // {:p} formats a reference as an address + // e.g. 0x7ffee3b2c490 +} +``` + +`{:p}` is the "pointer" format — it prints the memory address rather than the value. + +### Following the pointer — dereferencing + +Dereferencing (`*`) means "go to the address this pointer holds and give me what's there". + +```rust +fn main() { + let mut score: i32 = 50; + let r: &mut i32 = &mut score; // mutable reference — a pointer that allows writing + + println!("before: {}", score); // 50 + + *r = 99; // dereference + assign: go to the address, write 99 there + + println!("after : {}", score); // 99 ← `score` changed because `r` pointed at it +} +``` + +### Pointers to heap data — what `Box` looks like + +```rust +fn main() { + let b: Box = Box::new(7); + // b on the stack: [ 0x... ] ← an address + // | + // └──► [ 7 ] ← heap, at that address + + println!("b points to address : {:p}", b); // address on the heap + println!("value at that address: {}", *b); // 7 + + // Box is 8 bytes on a 64-bit system (just the address). + // The i32 itself is 4 bytes, living on the heap at that address. + println!("size of Box on stack: {} bytes", std::mem::size_of::>()); // 8 + println!("size of i32 on heap : {} bytes", std::mem::size_of::()); // 4 +} +``` + +### Visualising the relationship + +``` +Stack Heap +───────────────────── ────────────────── +b │ 0x55a3f1c0 │──────────► │ 7 │ (4 bytes at 0x55a3f1c0) + └────────────┘ └─────┘ + (8 bytes — just an address) +``` + +> **One sentence summary:** a pointer is just a number — a memory address. A Rust reference is a pointer that the compiler has verified is always valid, always properly aligned, and never aliased unsafely. + +--- + +## Quick-reference summary + +``` +OWNERSHIP + let x = value; x owns value + let y = x; ownership moves to y (x invalid for heap types) + let y = x.clone(); deep copy — both valid + +BORROWING + fn f(x: &T) immutable borrow — read only + fn f(x: &mut T) mutable borrow — read + write + +REFERENCES + &value create an immutable reference + &mut value create a mutable reference + Rules enforced at compile time: + - unlimited & at the same time + - only one &mut, and never alongside & + +SLICES + &s[0..n] reference to part of a string or vec +``` + +--- + +## Common beginner errors and fixes + +| Error message | What happened | Fix | +| ------------------------------------------------------------- | ------------------------------------------ | ----------------------------------------------------------------- | +| `value used after move` | You moved a value then tried to use it | Use `&` to borrow, or `.clone()` to copy | +| `cannot borrow as mutable` | You forgot `mut` on the variable | Change `let x` to `let mut x` | +| `cannot borrow as mutable because also borrowed as immutable` | You have `&` and `&mut` at the same time | End the immutable borrow before creating the mutable one | +| `does not live long enough` | A reference outlives the data it points to | Make sure the owned value lives at least as long as the reference | diff --git a/rust_session/notes/ownership.md b/rust_session/notes/ownership.md new file mode 100644 index 00000000..63c16e48 --- /dev/null +++ b/rust_session/notes/ownership.md @@ -0,0 +1,76 @@ +# Rust Ownership, Borrowing & References + +### A beginner's guide with annotated code + +--- + +## Why these concepts exist + +Most languages either make _you_ manage memory (C, C++) or use a garbage collector to do it (Python, Java, Go). Rust takes a third path: the **compiler** enforces memory safety at compile time through three rules. No runtime cost, no GC pauses, no dangling pointers. + +The three concepts build on each other: + +``` +Ownership → who is responsible for a value +Borrowing → letting someone else use it temporarily +References → the actual mechanism for borrowing +``` + +--- + +## 1. Ownership + +Every value in Rust has exactly **one owner**. When the owner goes out of scope, the value is dropped (freed). + +```rust +fn main() { + let name = String::from("Alice"); // `name` is the owner of this String + println!("{}", name); +} // `name` goes out of scope here → String is dropped automatically +``` + +### Ownership moves on assignment + +When you assign a heap value to another variable, ownership **moves** — the original variable becomes invalid. + +```rust +fn main() { + let a = String::from("hello"); + let b = a; // ownership moves from `a` to `b` + + // println!("{}", a); // ❌ compile error: `a` was moved + println!("{}", b); // ✅ `b` is the owner now +} +``` + +> **Why?** If both `a` and `b` owned the same heap memory, Rust would try to free it twice — a classic bug. Moving prevents this. + +### Ownership moves into functions too + +```rust +fn greet(s: String) { // `s` takes ownership + println!("Hello, {}!", s); +} // `s` is dropped here + +fn main() { + let name = String::from("Bob"); + greet(name); // ownership moves into `greet` + + // println!("{}", name); // ❌ compile error: `name` was moved +} +``` + +### Copy types are different + +Primitive types like `i32`, `bool`, `f64`, and `char` implement the `Copy` trait — they are copied, not moved, because they live entirely on the stack. + +```rust +fn main() { + let x = 5; + let y = x; // `x` is copied, not moved + + println!("{} {}", x, y); // ✅ both are valid — x is still usable +} +``` + +--- diff --git a/rust_session/notes/referencing.md b/rust_session/notes/referencing.md new file mode 100644 index 00000000..3f4f267c --- /dev/null +++ b/rust_session/notes/referencing.md @@ -0,0 +1,58 @@ +## 3. References + +A reference is a pointer that is _guaranteed by the compiler_ to be valid. No null pointers, no dangling pointers — ever. + +### Immutable reference (`&T`) + +```rust +fn print_length(s: &String) { + println!("Length: {}", s.len()); // can read + // s.push_str(" world"); // ❌ can't modify through &String +} + +fn main() { + let message = String::from("hello"); + + let r1 = &message; // first shared borrow + let r2 = &message; // second shared borrow — totally fine + + println!("{} and {}", r1, r2); // ✅ multiple readers are safe +} +``` + +### Mutable reference (`&mut T`) + +```rust +fn append_world(s: &mut String) { + s.push_str(", world"); // ✅ can modify through &mut +} + +fn main() { + let mut message = String::from("hello"); // must be `mut` to borrow mutably + append_world(&mut message); + println!("{}", message); // "hello, world" +} +``` + +### The exclusivity rule in action + +```rust +fn main() { + let mut s = String::from("hello"); + + let r1 = &s; // immutable borrow + let r2 = &s; // another immutable borrow — fine + // let r3 = &mut s; // ❌ can't have &mut while &s exists + + println!("{} {}", r1, r2); + // r1 and r2 are no longer used after this point + + let r3 = &mut s; // ✅ now fine — r1 and r2 are done + r3.push_str("!"); + println!("{}", r3); +} +``` + +> **The rule in plain English:** You can have as many readers as you like _or_ exactly one writer — never both at the same time. + +--- diff --git a/rust_session/student_registry/notes/borrowing_deep_dive.md b/rust_session/student_registry/notes/borrowing_deep_dive.md new file mode 100644 index 00000000..76411372 --- /dev/null +++ b/rust_session/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/student_registry/notes/core_logic.md b/rust_session/student_registry/notes/core_logic.md new file mode 100644 index 00000000..5383ef4e --- /dev/null +++ b/rust_session/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/student_registry/notes/referencing_deep_dive.md b/rust_session/student_registry/notes/referencing_deep_dive.md new file mode 100644 index 00000000..c422de29 --- /dev/null +++ b/rust_session/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/student_registry/notes/struct.md b/rust_session/student_registry/notes/struct.md new file mode 100644 index 00000000..162fa495 --- /dev/null +++ b/rust_session/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