diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..914f069 --- /dev/null +++ b/TODO.md @@ -0,0 +1,6 @@ +learn how to unit test +integration tests +how to work with the file system +how to make a lambda/anonymous function +traits +generics diff --git a/collections/.gitignore b/collections/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/collections/.gitignore @@ -0,0 +1 @@ +/target diff --git a/collections/Cargo.lock b/collections/Cargo.lock new file mode 100644 index 0000000..2a9936e --- /dev/null +++ b/collections/Cargo.lock @@ -0,0 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "collections" +version = "0.1.0" diff --git a/collections/Cargo.toml b/collections/Cargo.toml new file mode 100644 index 0000000..a17af8c --- /dev/null +++ b/collections/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "collections" +version = "0.1.0" +authors = ["unix"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/collections/src/main.rs b/collections/src/main.rs new file mode 100644 index 0000000..a8a3c54 --- /dev/null +++ b/collections/src/main.rs @@ -0,0 +1,127 @@ +use std::collections::hash_map::HashMap; + +fn main() { + let mut v = vec![1,2,3]; + + println!("2nd element in vector/heap array: {}", &v[1]); + + v[1] = 7; + + println!("2nd element in vector/heap array: {}", &v[1]); + + // println!("7th element does not exist and will panic: {}", &v[7]); + println!("7th element in vector/heap array: {:?}", v.get(7)); // returns an Option enum instead of panic + + match v.get(7) { + Some(x) => println!("found a value: {}", x), + None => println!("Couldn't find anything") + } + + v.push(73); // add an array element + + println!("array now looks like: {:?}", v); + + for i in &v { + println!("{}", i); + } + + for i in &mut v { + *i += 1; + println!("{}", i); + } + + let mut pickles = vec![Pickle{tasty:3}]; + + for p in &mut pickles { + p.tasty = 7; // equivalent to the next line + (*p).tasty = 7; // don't need to dereference this, rust will do it automatically + } + + println!("pickles array now looks like: {:#?}", pickles); + + + let mut ints: Vec = Vec::new(); + + ints.push(1); + ints.push(10); + + // Find the average of the ints + let mut total = 0; + for i in &ints { + total += i; + } + + let length = ints.len(); + match length { + 0 => println!("Average is 0"), + _ => println!("Average of ints: {}", total as usize / length) + } + + // Find the mean of the ints + + // First, sort the array + ints.push(-3); + ints.push(74); + + ints.sort(); + + let length = ints.len(); + + println!("the median is {:?} in {:?}", ints.get(length / 2), ints); + + // Find the mode of the array (the value that appears the most) + + ints.push(10); + ints.push(11); + ints.push(11); + ints.push(11); + + let mut map = HashMap::new(); + + for i in &ints { + let entry = map.entry(i).or_insert(0); + *entry += 1; // keep a count of how many times this thing shows up. + } + + println!("Here's the map: {:#?}", map); + + let mut max_key: i32 = 0; + let mut max_count: u32 = 0; + + for (k, v) in &map { + if *v > max_count { + max_key = **k; + max_count = *v; + } + } + + println!("the mode is {:?}", max_key); + + + // Convert strings to pig latin. + // The first consonant of each word is moved to the end of the word and “ay” is added, + // so “first” becomes “irst-fay.” Words that start with a vowel have “hay” added to + // the end instead (“apple” becomes “apple-hay”). + // Keep in mind the details about UTF-8 encoding! + + let original_string = String::from("first"); + + println!("converted string: {}", pig_latin(&original_string)); + println!("converted string: {}", pig_latin(&String::from("orange"))); +} + +fn pig_latin(s: &String) -> String { + let vowels = ["a","e","i","o","u"]; + let first_letter = &s[0..1]; + + if vowels.contains(&first_letter) { + return s.clone() + "-hay"; + } + + String::from(&s[1..]) + "-" + first_letter + "ay" +} + +#[derive(Debug)] // allows print +struct Pickle { + tasty: u32 +} \ No newline at end of file diff --git a/enums/.gitignore b/enums/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/enums/.gitignore @@ -0,0 +1 @@ +/target diff --git a/enums/Cargo.lock b/enums/Cargo.lock new file mode 100644 index 0000000..5daf92d --- /dev/null +++ b/enums/Cargo.lock @@ -0,0 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "enums" +version = "0.1.0" diff --git a/enums/Cargo.toml b/enums/Cargo.toml new file mode 100644 index 0000000..a382863 --- /dev/null +++ b/enums/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "enums" +version = "0.1.0" +authors = ["unix"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/enums/src/main.rs b/enums/src/main.rs new file mode 100644 index 0000000..30bca6e --- /dev/null +++ b/enums/src/main.rs @@ -0,0 +1,123 @@ +fn main() { + let male = SexKind::Male; + println!("sex 1: {:?}", male); + + let male = SexStatsKind::Male(String::from("penis"), 330, 401, 89); + println!("male w/ stats: {:?}", male); + + // call a method on the enum + let message = Message::Quit; + message.do_something(); + + + // The std lib comes with the Option enum + // so we can take care of traditional null values + // enum Option { + // Some(T), + // None, + // } + + // This is what the + let some_value = Option::Some("74"); + let some_value = Some("74"); + let no_value: Option = None; + + // Now we can use the match expression to take care + // of an Option enum, making sure to take into account + // a null case + + // match expression + println!("value of a dime is {}", value_in_cents(Coin::Dime)); + println!("value of an Alabama quarter is {}", value_in_cents(Coin::Quarter(UsState::Alabama))); + + // let's look at using option in a function definition + println!("value of 5 + 1 is {:?}", add_one(Some(5))); + println!("value of None + 1 is {:?}", add_one(None)); + + // if we have multiple arms that we don't need to cover we can use _ + let some_u8_value = 5u8; + match some_u8_value { + 1 => println!("one"), + 3 => println!("three"), + 5 => println!("five"), + 7 => println!("seven"), + _ => (), + } + + // instead of writing a match expression + // you can use if let instead + let coin = Coin::Dime; + + if let Coin::Quarter(state) = coin { + println!("State quarter from {:?}!", state); + } else if let Coin::Dime = coin { + println!("picllek"); + } else { + println!("No quarter..."); + } +} + +fn add_one(num: Option) -> Option { + match num { + None => None, + Some(i) => Some(i + 1) + } +} + +#[derive(Debug)] +enum UsState { + Alabama, + Alaska, +} + +enum Coin { + Penny, + Nickel, + Dime, + Quarter(UsState), +} + +fn value_in_cents(coin: Coin) -> u8 { + match coin { + Coin::Penny => { + // do some stuff here + 1 // return value + }, + Coin::Nickel => 5, + Coin::Dime => 10, + Coin::Quarter(state) => { + println!("the quarter is from {:?}", state); + 25 + }, + } +} + +#[derive(Debug)] +enum SexKind { + Male, + Female +} + +// We can associate data with the enums +// They can have different data +#[derive(Debug)] +enum SexStatsKind { + Male(String, u32, u32, u32), // You can even store a struct here (it's just a tuple of data) + Female(String, u32) +} + +// another example of structs +// Grouping possibilities and associated data is powerful when we pass data to functions and back. +enum Message { + Quit, + Move { x: i32, y: i32 }, // this is an anonymous struct + Write(String), + ChangeColor(i32, i32, i32), +} + +// You can even define methods on enums, just like structs +impl Message { + fn do_something(&self) { + // do something here... + } +} \ No newline at end of file diff --git a/errors/.gitignore b/errors/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/errors/.gitignore @@ -0,0 +1 @@ +/target diff --git a/errors/Cargo.lock b/errors/Cargo.lock new file mode 100644 index 0000000..2b4ab37 --- /dev/null +++ b/errors/Cargo.lock @@ -0,0 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "errors" +version = "0.1.0" diff --git a/errors/Cargo.toml b/errors/Cargo.toml new file mode 100644 index 0000000..d6e88c8 --- /dev/null +++ b/errors/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "errors" +version = "0.1.0" +authors = ["unix"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/errors/hello.txt b/errors/hello.txt new file mode 100644 index 0000000..d800886 --- /dev/null +++ b/errors/hello.txt @@ -0,0 +1 @@ +123 \ No newline at end of file diff --git a/errors/src/main.rs b/errors/src/main.rs new file mode 100644 index 0000000..c478086 --- /dev/null +++ b/errors/src/main.rs @@ -0,0 +1,86 @@ +use std::fs::File; +use std::fs::Error; +use std::io::ErrorKind; +use std::io; +use std::io::Read; + +fn main() { + let f = File::open("hello.txt"); + + match f { + Ok(file) => println!("We got the file {:?}", file), + Err(error) => println!("We got an error {}", error) + }; + + + let f = File::open("hello.txt"); + + // We could match on multiple errors + let f = match f { + Ok(file) => file, + Err(error) => match error.kind() { + ErrorKind::NotFound => match File::create("hello.txt") { + Ok(fc) => fc, + Err(e) => panic!("Problem creating the file: {:?}", e), + }, + other_error => { + panic!("Problem opening the file: {:?}", other_error) + }, + }, + }; + + // Cleaner to do it this way, so we don't have a bunch of match expressions. + let f = File::open("hello.txt").unwrap_or_else(|error| { + if error.kind() == ErrorKind::NotFound { + File::create("hello.txt").unwrap_or_else(|error| { + panic!("Problem creating the file: {:?}", error); + }) // unwraps the new created file or panics + } else { + panic!("Problem opening the file: {:?}", error); + } + }); + + + // unwrap the result or panic, not recommended for most use cases + // let f = File::open("doesnotexist.txt").unwrap(); + + // unwrap or provide a default for the type returned + // let f = File::open("doesnotexist.txt").unwrap_or(default: T); + + // fn default_fn(error: Error) -> File { + // File::create("pickles.txt").unwrap() + // } + + // provide a function that accepts the error and provides a default + // let f = File::open("doesnotexist.txt").unwrap_or_else(default_fn); + + // panic if there is an Ok value, return the err value + let error = File::open("doesnotexist.txt").unwrap_err(); + + + let good_year_from_input = "1909"; + let bad_year_from_input = "190blarg"; + let good_year: u32 = good_year_from_input.parse().unwrap_or_default(); + let bad_year: u32 = bad_year_from_input.parse().unwrap_or_default(); + + if bad_year > 0 { + println!(""); + } +} + + +fn read_username_from_file() -> Result { + let f = File::open("hello.txt"); + + let mut f = match f { + Ok(file) => file, + Err(e) => return Err(e), + }; + + let mut s = String::new(); + + match f.read_to_string(&mut s) { + Ok(_) => Ok(s), + Err(e) => Err(e), + } +} \ No newline at end of file diff --git a/functions/.gitignore b/functions/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/functions/.gitignore @@ -0,0 +1 @@ +/target diff --git a/functions/Cargo.lock b/functions/Cargo.lock new file mode 100644 index 0000000..68f0498 --- /dev/null +++ b/functions/Cargo.lock @@ -0,0 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "functions" +version = "0.1.0" diff --git a/functions/Cargo.toml b/functions/Cargo.toml new file mode 100644 index 0000000..fa48c49 --- /dev/null +++ b/functions/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "functions" +version = "0.1.0" +authors = ["unix"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/functions/src/main.rs b/functions/src/main.rs new file mode 100644 index 0000000..0244e94 --- /dev/null +++ b/functions/src/main.rs @@ -0,0 +1,94 @@ +fn main() { + println!("Hello, world!"); + + calculate_something(5, 6); + + println!("calculated: {}", calculate_something_return(7, 8)); + + let x = calculate_something_return(7, 8); + + if x < 10 { + println!("x is less than {}", 10); + } else { + println!("x is greater than 10"); + } + + let to_be = "it is to be"; + + // interesting, no compiler check if the same variable is compared against itself + let to_be = if to_be != to_be { "it is" } else { "it is not" }; + + println!("{} to be.", to_be); + + // basic loop + // loop { + // println!("loop forever! CTRL + c to quit"); + // } + + let mut counter = 0; + + let result = loop { + counter += 1; + + if counter == 10 { + // this is the only way to break out of a loop since there are no return statements + break counter * 2; + } + }; + + println!("The counter is now {}", result); + + + // woohoo while loops + let mut number = 5; + + while number > 0 { + number -= 1; + } + + println!("LIFTOFF!!!"); + + + // woohoo for loops + let a = [10, 20, 30, 40, 50]; + + for element in a.iter() { + println!("the value is: {}", element); + } + + let a = [1,3,8,190]; + + for e in a.iter() { + println!("for loop {}", e); + } + + // a range from 1 to 4 (exclusive) and then call reverse fn + for number in (1..4).rev() { + println!("{}!", number); + } + println!("LIFTOFF!!!"); + + // a range from 1 to 4 (inclusive) and then call reverse fn + for number in (1..=4).rev() { + println!("{}!", number); + } + println!("LIFTOFF!!!"); + + //ranges also work with char values but that's it + let alphabet = 'a'..='z'; + + for letter in alphabet { + println!("alphabet: {}", letter); + } +} + +fn calculate_something(x: i32, y: i32) { + println!("Calculating... {} + {} = {}", x, y, x + y); +} + +fn calculate_something_return(x: i32, y: i32) -> i32 { + // not that semicolons denote statements and statements do not return a value + // DO NOT PUT A SEMICOLON WHEN YOU WANT TO RETURN FROM AN EXPRESSION! + // {} create a new scope / expression + x + y +} diff --git a/guessing_game/.gitignore b/guessing_game/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/guessing_game/.gitignore @@ -0,0 +1 @@ +/target diff --git a/guessing_game/Cargo.lock b/guessing_game/Cargo.lock new file mode 100644 index 0000000..13359e9 --- /dev/null +++ b/guessing_game/Cargo.lock @@ -0,0 +1,85 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +dependencies = [ + "bitflags", +] + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + +[[package]] +name = "guessing_game" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "libc" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "265d751d31d6780a3f956bb5b8022feba2d94eeee5a84ba64f4212eedca42213" + +[[package]] +name = "rand" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" +dependencies = [ + "cloudabi", + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "winapi", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/guessing_game/Cargo.toml b/guessing_game/Cargo.toml new file mode 100644 index 0000000..f1b96fb --- /dev/null +++ b/guessing_game/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "guessing_game" +version = "0.1.0" +authors = ["Jonathan Lewis"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rand = "0.5.5" \ No newline at end of file diff --git a/guessing_game/src/main.rs b/guessing_game/src/main.rs new file mode 100644 index 0000000..aec24d2 --- /dev/null +++ b/guessing_game/src/main.rs @@ -0,0 +1,51 @@ +use std::io; +// Chapter 10 covers traits which is what Rng is. +use rand::Rng; +use std::cmp::Ordering; + + +fn main() { + println!("Guess the number!"); + println!("You have 10 tries to guess the number."); + + let secret_number = rand::thread_rng().gen_range(1, 100); + let mut guesses = 10; + + while guesses > 0 { + guesses -= 1; + + println!("Please input your guess!"); + + let mut guess = String::new(); + + // stores the inputted text into guess but then also returns a result + // the return is an enum of type io::Result and it can be Ok or an error + // we can then use unwrap() or expect("custom_message") to check for errors + io::stdin().read_line(&mut guess).expect("Failed to read line"); + + // trim off the extra new line at the end and parse to signed 32 bit int + let guess: i32 = match guess.trim().parse() { + Ok(num) => num, + Err(_) => { // If you want to match on a specific error I suggest viewing: https://stackoverflow.com/a/49783839 + println!("That is not a number, dummy!"); + continue + } + }; + + // For testing purposes, uncomment this + // println!("You guessed {} and the secret was {}", guess, secret_number); + + // match will be covered more in ch 6 and 18 + // seems like it's similar to a switch statement but ensures you take into account + // every scenario, so there's no choice in the matter, if there's a possible + // enum value, you have to handle it someway + match guess.cmp(&secret_number) { + Ordering::Less => println!("Too small!"), + Ordering::Greater => println!("Too big!"), // alt, put {} if you don't want to do anything + Ordering::Equal => { + println!("You win!"); + break; + }, + } + } +} diff --git a/hello/README.md b/hello/README.md new file mode 100644 index 0000000..78003e4 --- /dev/null +++ b/hello/README.md @@ -0,0 +1,13 @@ +In ubuntu 20.04, I had an issue with gcc not installed by default, make sure you run: + +``` +sudo apt-get update +sudo apt install build-essential +``` + +Run this project + +``` +rustc main.rs +./main +``` \ No newline at end of file diff --git a/hello/main b/hello/main new file mode 100644 index 0000000..178d526 Binary files /dev/null and b/hello/main differ diff --git a/hello/main.rs b/hello/main.rs new file mode 100644 index 0000000..d41f50b --- /dev/null +++ b/hello/main.rs @@ -0,0 +1,8 @@ +fn main() { + // The ! means it's calling a rust macro + // instead of a function. + println!("Hello, world!"); + + // interesting... semicolons are optional if followed by a } + // println!("Hello, world!") +} diff --git a/hello_cargo/.gitignore b/hello_cargo/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/hello_cargo/.gitignore @@ -0,0 +1 @@ +/target diff --git a/hello_cargo/Cargo.lock b/hello_cargo/Cargo.lock new file mode 100644 index 0000000..008df2f --- /dev/null +++ b/hello_cargo/Cargo.lock @@ -0,0 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "hello_cargo" +version = "0.1.0" diff --git a/hello_cargo/Cargo.toml b/hello_cargo/Cargo.toml new file mode 100644 index 0000000..8656966 --- /dev/null +++ b/hello_cargo/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "hello_cargo" +version = "0.1.0" +authors = ["unix"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/hello_cargo/README.md b/hello_cargo/README.md new file mode 100644 index 0000000..263f42e --- /dev/null +++ b/hello_cargo/README.md @@ -0,0 +1,32 @@ +Project created by: + +``` +cargo new hello_cargo +``` + +Build in debug mode without running. + +``` +cargo build +./target/debug/hello_cargo +``` + +Release build. + +``` +cargo build --release +./target/release/hello_cargo +``` + +Build and run. + +``` +cargo run +cargo run --release +``` + +Check if it compiles without building. This is often faster than building. + +``` +cargo check +``` \ No newline at end of file diff --git a/hello_cargo/src/main.rs b/hello_cargo/src/main.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/hello_cargo/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/modules/.gitignore b/modules/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/modules/.gitignore @@ -0,0 +1 @@ +/target diff --git a/modules/Cargo.lock b/modules/Cargo.lock new file mode 100644 index 0000000..a41bd32 --- /dev/null +++ b/modules/Cargo.lock @@ -0,0 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "modules" +version = "0.1.0" diff --git a/modules/Cargo.toml b/modules/Cargo.toml new file mode 100644 index 0000000..062cd08 --- /dev/null +++ b/modules/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "modules" +version = "0.1.0" +authors = ["unix"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/modules/src/back_of_house.rs b/modules/src/back_of_house.rs new file mode 100644 index 0000000..b54b54b --- /dev/null +++ b/modules/src/back_of_house.rs @@ -0,0 +1 @@ +pub mod kitchen_crew; \ No newline at end of file diff --git a/modules/src/back_of_house/kitchen_crew.rs b/modules/src/back_of_house/kitchen_crew.rs new file mode 100644 index 0000000..c62bc43 --- /dev/null +++ b/modules/src/back_of_house/kitchen_crew.rs @@ -0,0 +1,12 @@ +use crate::front_of_house::hosting; // as pickle; + +pub fn make_food(item: &String) { + println!("Prepping..."); + println!("Cooking..."); + println!("{} is ready!", item); + + // this should really go to a job queue and serve_food reads from the queue + // separation of concerns and event driven programming instead of making a hard link here + hosting::serve_food(item); + // pickle::serve_food(item); +} \ No newline at end of file diff --git a/modules/src/main.rs b/modules/src/main.rs new file mode 100644 index 0000000..c4c70ae --- /dev/null +++ b/modules/src/main.rs @@ -0,0 +1,37 @@ +mod front_of_house { + pub mod hosting { + pub fn add_to_waitlist(name: &String) { + println!("Added {} to waitlist", name); + } + + pub fn serve_food(item: &String) { + println!("Serving {} to table...", item); + } + } +} + +// We could define a mod here but as it grows +// we will want the functionality to be in different files + +// mod back_of_house { +// pub mod kitchen_crew { +// pub fn make_food(item: &String) { +// println!("Prepping..."); +// println!("Cooking..."); +// println!("{} is ready!", item); + +// // this should really go to a job queue and serve_food reads from the queue +// // separation of concerns and event driven programming instead of making a hard link here +// crate::front_of_house::hosting::serve_food(item); +// } +// } +// } + +// Defining back_of_house like this, we are now taking advantage of importing and then +// auto publicizing the module in one statement (make sure you have a semicolon at the end!) +mod back_of_house; + +fn main() { + front_of_house::hosting::add_to_waitlist(&String::from("Jon party of 1")); + back_of_house::kitchen_crew::make_food(&String::from("Fried Pickles")); +} diff --git a/ownership/.gitignore b/ownership/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/ownership/.gitignore @@ -0,0 +1 @@ +/target diff --git a/ownership/Cargo.lock b/ownership/Cargo.lock new file mode 100644 index 0000000..272786b --- /dev/null +++ b/ownership/Cargo.lock @@ -0,0 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "ownership" +version = "0.1.0" diff --git a/ownership/Cargo.toml b/ownership/Cargo.toml new file mode 100644 index 0000000..050dd8f --- /dev/null +++ b/ownership/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "ownership" +version = "0.1.0" +authors = ["unix"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/ownership/src/main.rs b/ownership/src/main.rs new file mode 100644 index 0000000..afdd747 --- /dev/null +++ b/ownership/src/main.rs @@ -0,0 +1,182 @@ +fn main() { + let s1 = "this is a primitive string and is not mutable"; + + println!("primitive string: {}", s1); + + println!("{}", recurse(7)); + + // Primitives and pointers live on the stack + // primitive values and pointers/references are pushed onto the stack for a function call + // all local variables are stored on the stock + // the pointer can point to data allocated on the heap + let mut s = String::from("hello"); + + s.push(' '); + s.push_str("pickles"); + + println!("{}", s); + + // make another pointer to s's heap value + // this makes s no longer valid + // because at the end of the scope, s and s2 will need to be drop'd + // so instead of freeing the same thing twice (bad) + // when we make another pointer to the same heap space + // we declare the old pointer as invalid + // in order to fix the situation + // the compiler will complain if you try to reference the old pointer + // below the new one + let mut s2 = s; + + s2.push('!'); + + println!("s2: {}", s2); + + // This won't compile for the reason above! + // println!("s: {}", s); + + // start a new scope with a new pointer to s2's data + { + let mut s3 = s2; + s3.push_str(" How tasty are you?"); + println!("s3: {}", s3); + } + + // s2 is still not valid here because s3 took ownership of the heap data + // println!("s2: {}", s2); + + let mut s4 = String::from("Mr. Pickles"); + let mut s5 = s4.clone(); + + // because s5 is a clone, we no longer have to worry about them both + // sharing the same heap space, they own their own spaces. + s4.push_str(" Garage"); + s5.push_str(" Haus"); + + println!("s4: {}, s5: {}", s4, s5); + + + // What happens when we pass a string to a function + // Well, because we make a copy to a function + // if we pass the pointer then our pointer locally + // is no longer valid + + let s6 = String::from("pickles"); + + takes_ownership(s6); + + // s6 is no longer valid in this scope because takes_ownership took it + // println!("s6: {}", s6); + + // we could have takes_ownership return it back to us when it's done + let mut s7 = String::from("pickles"); + + s7 = takes_ownership_v2(s7); + + println!("s7: {}", s7); + + // this is just a really weird way to do it but it can be done.... + // we could also return multiple values in a tuple kinda like javascript + + // but if we want to pass our heap variable to a function + // and we know the function will not make changes tot he variable + // well we can pass a reference and the compiler will make sure we + // don't change the variable in the function because it is borrowed + // and it only has read access and the variable will continue to be + // valid in this scope + let s8 = String::from("here"); + let length = borrows(&s8); + println!("s8: {} with a lenght of: {}", s8, length); // s8 is still valid here! + + // There is also dereferencing in ch 8 and 15 + // * is the deference symbol + + // strangely enough we can mutate a reference but we can only have one mutate reference in a scope at a time + // this doesn't work: + let s9 = String::from("can't change me"); + // cant_change_s9(&s9); // doesn't work + println!("s9: {}", s9); + + let mut s10 = String::from("you can change me... but only 1 per scope can!"); + can_change_s10(&mut s10); // passing to a function does not count towards the 2 mutable references + // can_change_s10(&s10); // doesn't work, you need to specify &mut mutable reference explicitly + println!("s10: {}", s10); + + // this shouldn't work because of data races so only one mutable reference per scope is allowed + let another_s10 = &mut s10; + // let another2_s10 = &mut s10; // this doesn't work because we can only have 1 reference at a time per scope + + // This also doesn't work + // { + // let another2_s10 = &mut s10; // this should work? Nope, because there would still be 2 in the this scope + // } + + println!("another_s10: {}", another_s10); + + another_s10.push_str("pickles"); // this works only BEFORE the immutable reference + + // you also can't make changes to a mutable reference while there is immutable refs out there + // because they don't expect the value to change. + let immut_s10 = &s10; + // another_s10.push_str("pickles"); // this works only BEFORE the immutable reference + // println!("s10: {}", another_s10); // we reference our mutable reference after the immutable ref... this doesn't work + + let mut s11 = String::from("phew"); + let r1_s11 = &s11; + let r2_s11 = &s11; + println!("s11, r1: {}, r2: {}", r1_s11, r2_s11); + let mr1_s11 = &mut s11; + println!("s11, mr1: {}", mr1_s11); + // println!("s11, r1: {}, r2: {}", r1_s11, r2_s11); // can't use the references after the mutable reference!!!! because the value might have changed! + + // NOTE: a reference's scope starts when it is declared and ends the last time it was referenced. This is slightly + // different from a variables/ownership scope. + + // Let's try to make a dangling reference which could get freed before our pointer is destroyed + // rust won't let you call the following function + // let reference_to_nothing = dangle(); +} + +// fn dangle() -> &String { +// let s = String::from("hello"); + +// &s // s is going out of scope and will be deallocated + +// // in order to solve this, you must return s directly +// // s (don't forget to change the return type to String) +// } + +fn can_change_s10(s: &mut String) { + s.push_str("... yeah it works"); +} + +// fn cant_change_s9(s: &String) { +// s.push_str("doesn't work :("); +// } + +// does not take ownership of the given parameter, only recieves a reference to the object instead +fn borrows(s: &String) -> usize { + let mut cloned = s.clone(); // we can still clone the reference but we can't change the original + cloned.push_str(" I am!"); + println!("cloned: {}", cloned); + s.len() +} + +// if we didn't want s to be mutable +// s: String is fine too +fn takes_ownership_v2(mut s: String) -> String { + s.push_str(" haus"); + println!("take ownership v2: {}", s); + s // return s +} + +fn takes_ownership(s: String) { + println!("take ownership: {}", s); +} + +fn recurse(x: i32) -> i32 { + if x * 3 > 30 || x * 3 < -30 { + return x + } + + recurse(x * 3) +} diff --git a/panic/.gitignore b/panic/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/panic/.gitignore @@ -0,0 +1 @@ +/target diff --git a/panic/Cargo.lock b/panic/Cargo.lock new file mode 100644 index 0000000..0a56721 --- /dev/null +++ b/panic/Cargo.lock @@ -0,0 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "panic" +version = "0.1.0" diff --git a/panic/Cargo.toml b/panic/Cargo.toml new file mode 100644 index 0000000..062c942 --- /dev/null +++ b/panic/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "panic" +version = "0.1.0" +authors = ["unix"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/panic/src/main.rs b/panic/src/main.rs new file mode 100644 index 0000000..6bf596c --- /dev/null +++ b/panic/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + panic!("We ran out of pickles"); +} diff --git a/rectangles/.gitignore b/rectangles/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/rectangles/.gitignore @@ -0,0 +1 @@ +/target diff --git a/rectangles/Cargo.lock b/rectangles/Cargo.lock new file mode 100644 index 0000000..4aabe7d --- /dev/null +++ b/rectangles/Cargo.lock @@ -0,0 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "rectangles" +version = "0.1.0" diff --git a/rectangles/Cargo.toml b/rectangles/Cargo.toml new file mode 100644 index 0000000..ae055b4 --- /dev/null +++ b/rectangles/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "rectangles" +version = "0.1.0" +authors = ["unix"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/rectangles/src/main.rs b/rectangles/src/main.rs new file mode 100644 index 0000000..3b49e6e --- /dev/null +++ b/rectangles/src/main.rs @@ -0,0 +1,80 @@ +fn main() { + let width = 700; + let height = 800; + + // we may want to pass a reference to area instead + // of the actual instance, otherwise area will take + // ownership + // println!("area is {}", area(Rectangle{width, height})) + + let r = Rectangle { + width, + height + }; + + println!("area is {}", r.area()); + + // try printing the rect... + println!("r is {:?}", r); // needs the debug trait + + println!("r with newlines is {:#?}", r); // needs the debug trait + + let r2 = Rectangle { + width: 50, + height: 50 + }; + + let r3 = Rectangle { + width: 800, + height: 800 + }; + + println!("can r1 fit in r: {}", r.can_hold(&r2)); + println!("can r2 fit in r: {}", r.can_hold(&r3)); + + let square = Rectangle::square(40); + println!("square: {:#?}", square); +} + +// We'll learn more about traits in ch 10 +#[derive(Debug)] // this is a trait? +struct Rectangle { + width: u32, + height: u32 +} + +// we can define multiple impl blocks too and that is valid +impl Rectangle { + // We can make other methods to transform self taking in &mut self + // It's not common to use self (without reference) because that + // would mean the original caller no longer has ownership. + fn area(&self) -> u32 { + self.width * self.height + } + + fn can_hold(&self, other: &Rectangle) -> bool { + self.width > other.width && self.height > other.height + } + + // associated function, String::from is also an associated fn + fn square(size: u32) -> Rectangle { + Rectangle { + width: size, + height: size + } + } +} + + +// AREA method is moved to rectangle type + +// Area of a rectangle. +// fn area(r: Rectangle) -> u32 { +// r.width * r.height +// } + +// Area of a rectangle. +// we take a ref so that we don't take ownership +// fn area(r: &Rectangle) -> u32 { +// r.width * r.height +// } \ No newline at end of file diff --git a/slices/.gitignore b/slices/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/slices/.gitignore @@ -0,0 +1 @@ +/target diff --git a/slices/Cargo.lock b/slices/Cargo.lock new file mode 100644 index 0000000..4d03e90 --- /dev/null +++ b/slices/Cargo.lock @@ -0,0 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "slices" +version = "0.1.0" diff --git a/slices/Cargo.toml b/slices/Cargo.toml new file mode 100644 index 0000000..722dd2d --- /dev/null +++ b/slices/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "slices" +version = "0.1.0" +authors = ["unix"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/slices/src/main.rs b/slices/src/main.rs new file mode 100644 index 0000000..a4ec18f --- /dev/null +++ b/slices/src/main.rs @@ -0,0 +1,51 @@ +fn main() { + let mut words = String::from("hello there"); + println!("{}", first_word(&words)); + + // slices + let hello = &words[0..5]; // rust will panic at runtime if this is out of bounds + let world = &words[6..11]; // these are immutable references to the words variable and thus guarantees that it won't be mutated + + let len = words.len(); + + let slice = &words[3..len]; + let slice = &words[3..]; // same as 3..len + let slice = &words[..3]; + + // you can't mutate the string here... + + println!("slices: '{}' and '{}'", hello, world); + + words.push_str("pickles"); + + println!("slices"); + + println!("using a string literal which is an str: {}", first_word("pickles sold here")); + + + println!("number slices"); + + let a = [1,2,5,8]; + + // we'll talk more on the various collection/slice types in ch 8 + let slice = &a[1..3]; // this slice is type &[i32] + + println!("1..3, first element is: {}, len is: {}", slice[0], slice.len()); +} + +// fn first_word(s: &String) -> &str { +fn first_word(s: &str) -> &str { + let bytes = s.as_bytes(); + + // we'll talk more on iter and enumerate in ch 13. + for (i, &item) in bytes.iter().enumerate() { + if item == b' ' { + return &s[..i]; // slice + } + } + + &s[..] // slice of the whole string + // s.len() // using length here is error prone because what if we change the original string? + // then it'll be out of sync + // what if we could use a slice! +} \ No newline at end of file diff --git a/structs/.gitignore b/structs/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/structs/.gitignore @@ -0,0 +1 @@ +/target diff --git a/structs/Cargo.lock b/structs/Cargo.lock new file mode 100644 index 0000000..5804b18 --- /dev/null +++ b/structs/Cargo.lock @@ -0,0 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "structs" +version = "0.1.0" diff --git a/structs/Cargo.toml b/structs/Cargo.toml new file mode 100644 index 0000000..1c06336 --- /dev/null +++ b/structs/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "structs" +version = "0.1.0" +authors = ["unix"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/structs/src/main.rs b/structs/src/main.rs new file mode 100644 index 0000000..f345d42 --- /dev/null +++ b/structs/src/main.rs @@ -0,0 +1,63 @@ +fn main() { + // You can't mark certain fields on a struct as mut + // Either it is wholly mut or not + let mut user = build_user(String::from("Jon"), String::from("jon@draplet")); + println!("email is {}", user.email); + + if !user.email.ends_with(".com") { + user.email.push_str(".com"); + } + + println!("email is now {}", user.email); + + user.pickle_count = 5; + + // Let's create a second user from the first + // Similar to JS, slightly different, doesn't update + // You set your specific fields first, remaining fields + // are set from your original + let user2 = User { + name: String::from("Mr Pickles"), + email: String::from("pickles@draplet.com"), + ..user + }; + + println!("user2 email is {} and they have {} pickles", user2.email, user2.pickle_count); + + // we can even have smaller structs, called tuple structs + // we only need to define the type name and field types in a tuple + // struct + // no need to name the fields! + let blue_color = Color(1,3,250); + let Color(r, g, b) = blue_color; // destructuing tuple struct + let red = blue_color.0; // access one member in the struct + println!("{}, {}, {}", r, g, b); + + // you can also have no members in your struct + // this may be useful when we add traits in ch 10 + struct PerformSomeTask(); + + let some_task = PerformSomeTask(); // woohoo +} + +struct Color(i32, i32, i32); + +fn build_user(name: String, email: String) -> User { + // instantiate a struct + User { + name, // we can use shorthand just like JS + email, + active: true, // + pickle_count: 0 + } +} + +// defining a struct +// Notice how we aren't storing references in our struct & +// this is on purpose, we can, but we need to learn lifetimes first +struct User { + name: String, + email: String, + active: bool, + pickle_count: u32 +} \ No newline at end of file diff --git a/variables/.gitignore b/variables/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/variables/.gitignore @@ -0,0 +1 @@ +/target diff --git a/variables/Cargo.lock b/variables/Cargo.lock new file mode 100644 index 0000000..360a5d8 --- /dev/null +++ b/variables/Cargo.lock @@ -0,0 +1,5 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "variables" +version = "0.1.0" diff --git a/variables/Cargo.toml b/variables/Cargo.toml new file mode 100644 index 0000000..a42e9c6 --- /dev/null +++ b/variables/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "variables" +version = "0.1.0" +authors = ["unix"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/variables/src/main.rs b/variables/src/main.rs new file mode 100644 index 0000000..7048459 --- /dev/null +++ b/variables/src/main.rs @@ -0,0 +1,75 @@ +const MAX_PICKLES: u32 = 100_000; // same as 100000 but easier to read when it's broken up w/ _'s + +fn main() { + // constants can't be set from a function call or any other means + // constants are constant for the life of the program and can be defined in any scope + const MAX_PICKLES_NO_UNDERSCORE: u32 = 100000; + + println!( + "The value of our constants is {} == {}", + MAX_PICKLES, MAX_PICKLES_NO_UNDERSCORE + ); + + let mut x = 5; + println!("The value of x is {}", x); + + x = 6; // x must be declared as mutable using 'mut' + println!("The value of x is {}", x); + + // Ok, can't change the type after assigned... cool + // x = "hey"; + // println!("The value of x is {}", x); + // x = 0.5; // can't change int to float or float to int... cool + // println!("The value of x is {}", x); + + // WOW, you can redeclare a variable... strange + // ok, I get it, this is called shadowing: https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#shadowing + // let x = 6; + // println!("The value of x is {}", x); + + // Here's a good example of shadowing and changing the type of the variable we are shadowing + // shadowing allows us to create new variables with different types without having to name + // things differently like num_spaces, str_spaces + let spaces = " "; + println!("spaces is '{}'", spaces); + + let spaces = spaces.len(); + println!("# of spaces is {}", spaces); + + // data types + + // we need to tell the compile what kind of number we expect from parsing since + // it doesn't know what kind of number it will be. + // Unsigned 32 bit int + // TODO I'm not sure what this expect function is... + let answer_to_the_universe: u32 = "42".parse().expect("This is not a number!"); + println!("universe is {}", answer_to_the_universe); + + let x: i32 = 2 - 4; + // this would produce an overflow error because negative numbers must be signed + // let x: u32 = 2 - 4; + + // If you would like to perform overflow on purpose: https://doc.rust-lang.org/book/ch03-02-data-types.html#integer-overflow + + println!("signed number {}", x); + + // Addition must be with the same types + // println!("float + int is {}", 64 + 3.3); + + let heart_face: char = '🥰'; + + println!("I {1} {0}", "Rust!", heart_face); + + // tuples + let tup: (i32, f64, u8) = (500, 6.4, 1); + + let (x, y, z) = tup; + + println!("{} is the same as {}, {} {}", tup.0, x, y, z); + + // arrays + let a: [i32; 5] = [1, 2, 3, 4, 5]; + let a = [3; 5]; // five elements that will be set to 3 e.g. [3,3,3,3,3] + + println!("a[0] is {}", a[0]); +}