diff --git a/.gitignore b/.gitignore index de693c8..f7957b1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ main.exe main.pdb - -# Added by cargo +## Added by cargo ## /target diff --git a/10_loops/main.rs b/10_loops/main.rs index 5ee850f..f9a3fba 100644 --- a/10_loops/main.rs +++ b/10_loops/main.rs @@ -15,9 +15,9 @@ fn main(){ // Loop keyword fn loop_fn(){ - let mut counter = 8; + let mut counter = 7; - let result = loop{ + let result = loop{2 counter += 1; diff --git a/15_mini_project/main.rs b/15_mini_project/main.rs new file mode 100644 index 0000000..73c3429 --- /dev/null +++ b/15_mini_project/main.rs @@ -0,0 +1,137 @@ +// A Rust mini-project demonstrating all core concepts from this crash course! +// We will write a Bookstore Inventory CLI as our mini project. +// This makes use of variables, constants, structs, enums, ownership, borrowing, functions, conditionals, loops, vectors, hashmaps, error handling, and more. + +use std::collections::HashMap; +//std::collections::HashMap is a module that provides a hashmap data structure +use std::io; +//std::io is a module that provides input and output operations + +// Constants +const MAX_STOCK: u32 = 100; // 100 books in the inventory + +// Enum (for Book genre) +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +enum Genre { + Fiction, + NonFiction, + Science, + Fantasy, +} + +// Struct (for Book) +#[derive(Debug)] +struct Book { + title: String, + author: String, + genre: Genre, + pages: u32, +} + +// Ownership, Borrowing, Compound Types, Collections +struct Inventory { + stock: HashMap>, +} + +impl Inventory { + fn new() -> Self { + Inventory { + stock: HashMap::new(), + } + } + + fn add_book(&mut self, book: Book) -> Result<(), String> { + let entry = self.stock.entry(book.genre).or_insert(Vec::new()); + if entry.len() >= MAX_STOCK as usize { + return Err(format!("Cannot add more books to genre {:?} (stock limit reached)", book.genre)); + } + entry.push(book); + Ok(()) + } + + fn list_books(&self, genre: Option) { + println!("--- INVENTORY ---"); + for (g, books) in &self.stock { + if let Some(target) = genre { + if *g != target { continue; } + } + println!("\nGenre: {:?}", g); + for (i, b) in books.iter().enumerate() { + println!("{}. '{}' by {}, {} pages", i+1, b.title, b.author, b.pages); + } + } + } +} + +fn parse_genre(input: &str) -> Option { + match input.trim().to_lowercase().as_str() { + "fiction" => Some(Genre::Fiction), + "nonfiction" => Some(Genre::NonFiction), + "science" => Some(Genre::Science), + "fantasy" => Some(Genre::Fantasy), + _ => None, + } +} + +fn main() { + let mut inventory = Inventory::new(); + println!("Welcome to the Rust Bookstore Inventory CLI!"); + + loop { + // Demonstrate shadowing + let mut action = String::new(); + println!("\nChoose action: [add/list/quit]: "); + io::stdin().read_line(&mut action).expect("Failed to read"); + let action = action.trim(); // shadowing action + + if action == "quit" { + println!("Goodbye!"); + break; + } else if action == "add" { + // Adding a book (Ownership, references/borrowing for input) + let mut title = String::new(); + let mut author = String::new(); + let mut genre_str = String::new(); + let mut pages_str = String::new(); + + println!("Title: "); io::stdin().read_line(&mut title).unwrap(); + println!("Author: "); io::stdin().read_line(&mut author).unwrap(); + println!("Genre (fiction/nonfiction/science/fantasy): "); io::stdin().read_line(&mut genre_str).unwrap(); + println!("Pages: "); io::stdin().read_line(&mut pages_str).unwrap(); + + let genre = match parse_genre(&genre_str) { + Some(g) => g, + None => { + println!("Invalid genre!"); + continue; + } + }; + let pages: u32 = match pages_str.trim().parse() { + Ok(v) => v, + Err(_) => { + println!("Pages must be a number!"); + continue; + } + }; + let book = Book { + title: title.trim().to_string(), + author: author.trim().to_string(), + genre, + pages, + }; + // Error handling with Result + match inventory.add_book(book) { + Ok(_) => println!("Book added to inventory!"), + Err(e) => println!("Error: {}", e), + } + } else if action == "list" { + let mut filter = String::new(); + println!("Filter by genre (press Enter to show all): "); + io::stdin().read_line(&mut filter).unwrap(); + let genre = parse_genre(&filter); + inventory.list_books(genre); + } else { + println!("Invalid action! Try again."); + } + } +} diff --git a/1_primitive_data_types/main.rs b/1_primitive_data_types/main.rs index c4cb277..b5b3cfc 100644 --- a/1_primitive_data_types/main.rs +++ b/1_primitive_data_types/main.rs @@ -1,19 +1,26 @@ -// Primitive data types -// int, float, bool, char +// Primitive data types are the basic building blocks of the language +// integers, floats, booleans, characters: + +// Integers are used to store whole numbers +// Floats are used to store decimal numbers +// Booleans are used to store true or false values +// Characters are used to store single characters fn main() { println!("Hello, world!"); - // Integer - // Rust has signed (+ & -) and unsigned integer (only '+') types of different sizes + // Integers + // Rust has signed (+ & -) and unsigned integers (only '+') types of different sizes // i8, i16, i32, i64, i128: Signed integers - // u8, i16, u32, u64, u128: Unsigned integers - + // u8, u16, u32, u64, u128: Unsigned integers + let x = -42; let y: u8 = 100; + let z = -123_i16; println!("Signed Integer: {}", x); println!("Unsigned Integer: {}", y); - + println!("Signed Integer: {}", z); + // Floats [floating point types] // f32, f64 let pi: f64 = 3.14; diff --git a/2_compound_data_types/main.rs b/2_compound_data_types/main.rs index 64ddc7b..9a932f3 100644 --- a/2_compound_data_types/main.rs +++ b/2_compound_data_types/main.rs @@ -1,18 +1,31 @@ // Compound Data Types // Arrays, tuples, slices and strings (slice string) +// Arrays are fixed size, ordered, and homogeneous (all elements must be of the same type) +// Tuples are fixed size, ordered, and heterogeneous (elements can be of different types) +// Slices are views into a contiguous sequence of elements +// Strings are growable and mutable + +// Which ones are stored on the stack and which ones are stored on the heap? +// Arrays are stored on the stack +// Tuples are stored on the stack +// Slices are stored on the stack +// Strings are stored on the heap fn main() { // Arrays let numbers: [i32; 5] = [1, 2, 3, 4, 5]; println!("Array of numbers: {:?}", numbers); - + // &str is a string slice + // it's a pointer to a string in the heap let fruits: [&str; 2] = ["Apple", "Orange"]; println!("Array of fruits: {:?}", fruits); + // {:?} is a placeholder for the array // Tuples let bio_data: (String, i32, bool) = ("Alice".to_string(), 30, false); println!("Bio data Tuple: {:?}", bio_data); + // .to.string() is a method that converts a string literal to a string let mixed_tuples = ("Joe", 23, true, [1, 2, 3, 4, 5]); println!("Mixed Tuple: {:?} \n", mixed_tuples); @@ -35,6 +48,11 @@ fn main() { // immutable string let name: String = String::from("John"); println!("Name is {}", name); + // String::from is a method that converts a string literal to a string + // what is the difference between to_string() and String::from()? + // to_string() is a method that converts a string literal to a string + // String::from() is a function that converts a string literal to a string + // String::from() is more efficient than to_string() because it does not allocate memory on the heap // Mutable string let mut full_name: String = String::from("Joe"); diff --git a/3_functions/main.rs b/3_functions/main.rs index c2e1ab2..08930e1 100644 --- a/3_functions/main.rs +++ b/3_functions/main.rs @@ -35,13 +35,11 @@ fn bmi_calculator(weight: f64, height: f64) -> f64 { // Expressions and Statements // Expression: Anything that returns a value. -/* - * let X = { - * let a = 50; - * let b = 10; - * b + a - * } - */ +let X = { + let a = 50; + let b = 10; + b + a +}; // Statement: Anything that does not return a value. // let x = 10; // Other examples includes function declaration themselves and conditional statements \ No newline at end of file diff --git a/4_ownership/main.rs b/4_ownership/main.rs index 6768aad..f98a60c 100644 --- a/4_ownership/main.rs +++ b/4_ownership/main.rs @@ -1,28 +1,30 @@ -// Ownership -// Solves the issue created by Garbage collections and manual memory handling +/* +Ownership +Solves the issue created by garbage collection and manual memory handling +OWNERSHIP introduced by Rust solves memory safety issues and high performance at the same time. +What is Ownership ? +Every value has a single owner [every variable has one value, and it is its sole owner]. +*/ -// Ownership -// [stopping/Resuming the program] -// OWNERSHIP introduced by Rust to solve memory safety issues and high performance at the same time. -// What is Ownership ? -// Every value has a single owner [every variable has one value, and it is its sole owner]. - -// Ownership Rules -// 1- Each value in Rust has a variable that's its owner. -// 2- There can be only one owner at a time. -// 3— When the owner goes out of scope, the value will be dropped. +/* +Ownership Rules +1- Each value has a variable that is its owner. +2- There can be only one owner at a time. +3— When the owner goes out of scope, the value will be dropped. +*/ fn main(){ // s1 is the owner of this string value let s1: String = String::from("Hello"); + // String::from is a function that creates a String from a string literal and a string literal is a fixed size + // String is a heap allocated data structure that is growable and mutable // Borrow reference to s1 let len = calculate_str_length(&s1); println!("Length of '{}' is {}", s1, len); let s2 = s1; - println!("Value: {}", s2); - println!("Value: {}", s2); + println!("Value of s2: {}", s2); /* * There can only be one owner at a time @@ -33,4 +35,6 @@ fn main(){ fn calculate_str_length(s: &String) -> usize { s.len() + // s.len() is a method that returns the length of the string + // -> usize is the return type of the function } \ No newline at end of file diff --git a/5_references_and_borrowing/main.rs b/5_references_and_borrowing/main.rs index b1731cc..062c3cd 100644 --- a/5_references_and_borrowing/main.rs +++ b/5_references_and_borrowing/main.rs @@ -29,9 +29,9 @@ struct BankAccount{ owner: String, balance: f64 } - +//impl is a keyword that implements the methods for the BankAccount struct impl BankAccount{ - fn withdraw(&mut self, amount: f64){ + fn withdraw(&mut self, amount: f64){ // self is a reference to the BankAccount struct println!("Withdrawing {} from account owned by {}", amount, self.owner); self.balance -= amount; } diff --git a/README.md b/README.md index d1968c0..b4c4cdd 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ To run a lesson, just navigate into the directory and use the `make run` command #### Example -If you want to run the `1_primivitive_data_types` lesson. Use the command below. +If you want to run the `1_primitive_data_types` lesson. Use the command below. ```bash # We are currently inside the `rust-full-course` directory diff --git a/main b/main new file mode 100755 index 0000000..490eabb Binary files /dev/null and b/main differ