diff --git a/README.md b/README.md new file mode 100644 index 0000000..e692631 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Rust-Guide-Projects + +This is a collection of small projects and examples made while learning along with "Rust: The Complete Developer's Guide" by Stephen Grider. diff --git a/p01-deck/Cargo.lock b/p01-deck/Cargo.lock index c93f077..74f131a 100644 --- a/p01-deck/Cargo.lock +++ b/p01-deck/Cargo.lock @@ -2,6 +2,74 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + [[package]] name = "p01-deck" version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/p01-deck/Cargo.toml b/p01-deck/Cargo.toml index 75e6c3a..33059fa 100644 --- a/p01-deck/Cargo.toml +++ b/p01-deck/Cargo.toml @@ -4,3 +4,4 @@ version = "0.1.0" edition = "2021" [dependencies] +rand = "0.8.5" diff --git a/p01-deck/README.md b/p01-deck/README.md new file mode 100644 index 0000000..00316b4 --- /dev/null +++ b/p01-deck/README.md @@ -0,0 +1,5 @@ +# p01-deck + +A data object made to represent a deck of cards, done to give examples of basics in Rust such as Structs, String Formatting, Struct Attributes, Vectors and Arrays, Inherent Implementation, and others. + +When the project was completed, comments were cleaned up in the final main.rs, but main.verbosecomment.rs was made with intentionally verbose comments left in for explaining and reminding of details about the code as they were being introduced by the instructor. diff --git a/p01-deck/src/main.rs b/p01-deck/src/main.rs index a98eea3..6ea3327 100644 --- a/p01-deck/src/main.rs +++ b/p01-deck/src/main.rs @@ -1,3 +1,4 @@ +use rand::{seq::SliceRandom, thread_rng}; /* objective: @@ -12,19 +13,61 @@ #[derive(Debug)] struct Deck { - cards: Vec, //vectors can grow and shrink in size, while arrays have fixed lengths + cards: Vec, +} + +impl Deck { + fn new() -> Self { + let suits = ["Hearts", "Diamonds", "Clubs", "Spades"]; + let values = [ + "Ace", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Jack", + "Queen", "King", + ]; + + let mut cards = vec![]; + + for suit in suits { + for value in values { + let card = format!("{} of {}", value, suit); + cards.push(card); + } + } + + Deck { cards } + } + + fn shuffle(&mut self) { + let mut rng = thread_rng(); + + self.cards.shuffle(&mut rng); + } + + fn deal(&mut self, mut num_cards: usize) -> Vec { + if num_cards > self.cards.len() { + num_cards = self.cards.len(); + } + self.cards.split_off(self.cards.len() - num_cards) + } } fn main() { - //in Rust, variables are referred to as 'bindings' - //another equivalent way to create the empty vector would be Deck { cards: Vec::new() } - let deck = Deck { cards: vec![] }; - - //the :? inside the {} is the Debug formatter - //which needs #[derive(Debug)] added to the struct Deck to make function without an error - //but now it is able to print this struct to the console - println!("Here's your deck: {:?}", deck); - - // another way of expressing that formatted string is to put the binding name in the {} - // println!("Here's your deck: {deck}"); + let mut deck = Deck::new(); + + deck.shuffle(); + + println!("Here's your deck: {:#?}", deck); + + //enabled error handling for asking for too many cards, + //but asking for a negative number of cards gives an error + //it is left as-is for now because error handling will be addressed in a coming project + let number_of_cards = 5; + + let some_cards = deck.deal(number_of_cards); + + println!("Dealt {} cards: {:#?}", some_cards.len(), some_cards); + println!( + "There are {} cards remaining in the deck: {:#?}", + deck.cards.len(), + deck + ); } diff --git a/p01-deck/src/main.verbosecomment.rs b/p01-deck/src/main.verbosecomment.rs new file mode 100644 index 0000000..bddc6d8 --- /dev/null +++ b/p01-deck/src/main.verbosecomment.rs @@ -0,0 +1,124 @@ +//this enables a shorthand way to access thread_rng from the rand dependency, and SliceRandom from rand::seq +//the auto formatter rearranged it so seq::SliceRandom came first before thread_rng +use rand::{seq::SliceRandom, thread_rng}; +/* + objective: + + make a data object that can represent a deck of cards + + give it a new() method that creates a set of 52 cards + give it a shuffle() method that randomizes the order of the cards + give it a deal() method that returns one card + + this can be represented in a struct, which in rust is similar to classes in other languages +*/ + +//This #[derive(Debug)] statement is giving the struct an attribute, something where 'derive' +//is adding instructions at compile time to this struct, which enables it to have the Debug trait, +//which allows it to work in a formatted string using a syntax like "Here is your deck: {:?}", deck +#[derive(Debug)] +struct Deck { + //vectors can grow and shrink in size, while arrays have fixed lengths + cards: Vec, +} + +//this is an inherent implementation block, which seems to be used to give the Deck struct functions +//it is not yet clear why this isn't just defined inside the struct itself but I'd like to know why that is so +//update: it seems it is so that data structure and struct behaviours can be separated for readability & maintainability +impl Deck { + //the key word Self in this case is a reference to the type in the parenting inherent implementation block, + //so Self here will return type Deck + fn new() -> Self { + //Rather than typing out all the cards one at a time, it can be done as a nested for loop + + //these are arrays and have a fixed length + //the auto formatter broke the array declaration into multiple lines and added a trailing comma + let suits = ["Hearts", "Diamonds", "Clubs", "Spades"]; + let values = [ + "Ace", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Jack", + "Queen", "King", + ]; + + //this is a vec![] because it will be given elements dynamically + //cards.push() was giving an error about immutability so mut was added here to resolve that + let mut cards = vec![]; + + //these for loops work on arrays, but when I tried making suits and values vectors, there was an error given here + //The error said: `values` moved due to this implicit call to `.into_iter()`, in previous iteration of looprustcE0382 + //It didn't seem to mind that suits was a vecotr, only values. + //This was resolved anyway by using arrays instead. + for suit in suits { + for value in values { + let card = format!("{} of {}", value, suit); + cards.push(card); + } + } + + //in Rust, variables are referred to as 'bindings' + //an empty vector can be created using a macro like vec![] + //another equivalent way to create the empty vector would be Deck { cards: Vec::new() } + // let deck = Deck { cards: vec![] }; + + //now that the cards vector exists it can be given to this struct literal instead of an empty vector + // let deck = Deck { cards: cards }; + + //but the rust-analyzer gives a hint that the shorthand can be used here because the field and binding have identical names + // let deck = Deck { cards }; + // return deck; + + //this line is an equivalent result to the prior return statement above + // return Deck { cards }; + + //this is an IMPLICIT RETURN statement and is equivalent to the two prior return statements shown above + //it is important that it does NOT have a semicolon on it for it to work as an implicit return + //Rust will automatically return the last evaluated expression in a function IF IT DOESN'T HAVE A SEMICOLON on it + Deck { cards } + } + + //&self needs to be changed to &mut self because the deck being referenced by &self will change + fn shuffle(&mut self) { + let mut rng = thread_rng(); + + //this shuffle method comes from using rand::seq::SliceRandom which modifies vectors to have a shuffle method + //the &mut rng is a reference to the rng object which is intended to be mutable + self.cards.shuffle(&mut rng); + } + + fn deal(&mut self, mut num_cards: usize) -> Vec { + if num_cards > self.cards.len() { + num_cards = self.cards.len(); + } + self.cards.split_off(self.cards.len() - num_cards) + } +} + +fn main() { + //while a struct seemed to need to be assigned as a struct literal, like seen in 'let deck = Deck { cards }, + //an inherent implementation or 'impl Deck {fn new() -> Self{}}' was able to allow making a constructor to use like seen here + //deck needs to be mutable because it will be altered when it is shuffled + let mut deck = Deck::new(); + + deck.shuffle(); + + //the :? inside the {} is the Debug formatter + //which needs #[derive(Debug)] added to the struct Deck to make function without an error + //but now it is able to print this struct to the console + //adding the # to the formatter making it {:#?} makes it print in a 'pretty' way which is easier for a human to read + println!("Here's your deck: {:#?}", deck); + + //enabled error handling for asking for too many cards, + //but asking for a negative number of cards gives an error + let number_of_cards = 5; + + let some_cards = deck.deal(number_of_cards); + + println!("Dealt {} cards: {:#?}", some_cards.len(), some_cards); + println!( + "There are {} cards remaining in the deck: {:#?}", + deck.cards.len(), + deck + ); + + // another way of expressing that formatted string is to put the binding name in the {} + // println!("Here's your deck: {deck:#?}"); +} diff --git a/p02-bank/Cargo.lock b/p02-bank/Cargo.lock new file mode 100644 index 0000000..5481d0a --- /dev/null +++ b/p02-bank/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "p02-bank" +version = "0.1.0" diff --git a/p02-bank/Cargo.toml b/p02-bank/Cargo.toml new file mode 100644 index 0000000..7c8ed2e --- /dev/null +++ b/p02-bank/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "p02-bank" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/p02-bank/README.md b/p02-bank/README.md new file mode 100644 index 0000000..daa2adb --- /dev/null +++ b/p02-bank/README.md @@ -0,0 +1,5 @@ +# p02-bank + +A simulation of a bank, as a simple design where a Bank struct can have a list of Account structs, with some functionality for each, such as 'create new', 'add to balance', 'deduct from balance', 'show balance', etc. + +The example is kept simple because it will be used to show important topics about Rust's Ownership and Borrowing features. diff --git a/p02-bank/src/main.rs b/p02-bank/src/main.rs new file mode 100644 index 0000000..453a4a1 --- /dev/null +++ b/p02-bank/src/main.rs @@ -0,0 +1,106 @@ +#[derive(Debug)] +struct Account { + id: u32, + balance: isize, //balance will represent $10.23 as 1023 so integer is used + holder: String, +} + +impl Account { + fn new(id: u32, holder: String) -> Self { + Account { + id, + holder, + balance: 0, + } + } + + ///Increase account balance by specified deposit size if deposit >= 0. Return new account balance. + fn credit_funds(&mut self, deposit: isize) -> isize { + if deposit >= 0 { + println!("Depositing {}", deposit); + self.balance += deposit; + println!("Deposit complete, new balance: {}", self.balance); + } + self.balance + } + + ///Decrease account balance by specified withdrawal size if withdrawal >= 0 and withdrawal < account balance. Return new account balance. + fn debit_funds(&mut self, withdrawal: isize) -> isize { + if withdrawal >= 0 && withdrawal < self.balance { + println!("Withdrawing {}", withdrawal); + self.balance -= withdrawal; + println!("Withdrawal complete, new balance: {}", self.balance); + } + if withdrawal > self.balance { + println!("Insufficient funds"); + } + self.balance + } + + ///Returns formatted string of account fields and values. + fn account_summary(&self) -> String { + format!("{:?}", self) + } +} + +#[derive(Debug)] +struct Bank { + accounts: Vec, +} + +impl Bank { + fn new() -> Self { + Bank { accounts: vec![] } + } +} + +fn main() { + let bank = Bank::new(); + + //putting in a string like "" is seen as &str, or a string slice, + //so to get a true String type you need to use String::from(), or format!() + let mut account = Account::new(1, String::from("TestName")); + + println!("{}", account.account_summary()); + + account.credit_funds(10); + + account.debit_funds(5); + + account.debit_funds(15); + + println!("{}", account.account_summary()); + + println!("Bank {bank:#?}"); + + /* + + The following code is being left as a reminder comment + + This was the main point of this project, to demonstrate + that Rust has ownership, borrowing, lifetime features + Most of the videos were about showing examples of it and + doing exercises to practice understanding of it + + before watching the final videos where he shows implementing the functions, + I wanted to try doing them myself first in a branch and then depending on + how close my version is to what he shows, I'll either merge the branch over + or else leave it as an unmerged branch and go on to make the main branch + follow the guide's examples + + //printing the account once, no error + print_account(account); + //printing the account a second time, an error occurs + //it has something to do with the ownership / borrowing feature in rust + print_account(account); + /* + error information: + use of moved value: `account` + value used here after moverustc Click for full compiler diagnostic + main.rs(43, 19): value moved here + main.rs(38, 9): move occurs because `account` has type `Account`, which does not implement the `Copy` trait + main.rs(29, 27): consider changing this parameter type in function `print_account` to borrow instead if owning the value isn't necessary + main.rs(2, 1): if `Account` implemented `Clone`, you could clone the valu + */ + */ +}