├── .gitignore ├── 01-hello-cargo ├── Cargo.toml ├── README.md ├── commands.md └── src │ ├── arrays.rs │ ├── cli.rs │ ├── conditionals.rs │ ├── enums.rs │ ├── functions.rs │ ├── loops.rs │ ├── main.rs │ ├── pointer_ref.rs │ ├── print.rs │ ├── strings.rs │ ├── structs.rs │ ├── tuples.rs │ ├── types.rs │ ├── vars.rs │ └── vectors.rs ├── 02-guessing-game ├── Cargo.toml ├── README.md └── src │ └── main.rs ├── 03-minigrep ├── Cargo.toml ├── README.md ├── output.txt ├── poem.txt └── src │ ├── lib.rs │ └── main.rs ├── 04-hello ├── 404.html ├── Cargo.toml ├── README.md ├── hello.html └── src │ ├── bin │ └── main.rs │ └── lib.rs ├── 05-game-of-life ├── .appveyor.yml ├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE_APACHE ├── LICENSE_MIT ├── README.md ├── after.txt ├── before.txt ├── benches │ └── bench.rs ├── commands.md ├── screenshot.png ├── src │ ├── lib.rs │ └── utils.rs ├── tests │ └── web.rs └── www │ ├── .bin │ └── create-wasm-app.js │ ├── .gitignore │ ├── .travis.yml │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ ├── bootstrap.js │ ├── index.html │ ├── index.js │ ├── package-lock.json │ ├── package.json │ └── webpack.config.js ├── README.md └── playground ├── 01-rust-sandbox ├── commands.md └── hello.rs ├── 02-programming-concepts ├── Cargo.toml └── src │ ├── control.rs │ ├── functions.rs │ ├── main.rs │ ├── types.rs │ └── vars.rs ├── 03-ownership ├── Cargo.toml └── src │ ├── basics.rs │ ├── main.rs │ ├── references.rs │ └── slices.rs ├── 04-structs ├── Cargo.toml └── src │ ├── basics.rs │ ├── main.rs │ ├── method.rs │ └── rectangles.rs ├── 05-enums ├── Cargo.toml └── src │ ├── definition.rs │ ├── if_let.rs │ ├── main.rs │ └── match_operator.rs ├── 06-managing-projects ├── Cargo.toml └── src │ ├── front_of_house.rs │ ├── front_of_house │ └── hosting.rs │ ├── lib.rs │ └── main.rs ├── 07-collections ├── Cargo.toml └── src │ ├── hash_maps.rs │ ├── main.rs │ ├── strings.rs │ └── vectors.rs ├── 08-error-handling ├── Cargo.toml ├── hello.txt └── src │ ├── main.rs │ ├── recoverable_errors.rs │ └── unrecoverable_errors.rs ├── 09-generic-types ├── Cargo.toml └── src │ ├── duplicate.rs │ ├── generic_data_types.rs │ ├── lifetimes.rs │ ├── main.rs │ └── traits.rs ├── 10-automated-tests ├── Cargo.toml ├── src │ └── lib.rs └── tests │ ├── common │ └── mod.rs │ └── integration_test.rs ├── 11-iterators-closures ├── Cargo.toml └── src │ ├── closures.rs │ ├── iterators.rs │ ├── lib.rs │ └── main.rs ├── 12-more-cargo-crates ├── Cargo.toml └── src │ ├── lib.rs │ ├── lib_example.rs │ └── main.rs ├── 13-cargo-workspace ├── Cargo.toml ├── add_one │ ├── Cargo.toml │ └── src │ │ └── lib.rs └── adder │ ├── Cargo.toml │ └── src │ └── main.rs ├── 14-smart-pointers ├── Cargo.toml └── src │ ├── boxes.rs │ ├── dereference.rs │ ├── drop.rs │ ├── lib.rs │ ├── main.rs │ ├── ref_counting.rs │ └── ref_cycles.rs ├── 15-concurrency ├── Cargo.toml └── src │ ├── channels.rs │ ├── main.rs │ ├── shared_state.rs │ └── threads.rs ├── 16-oop-features ├── Cargo.toml └── src │ ├── lib.rs │ ├── lib_basics.rs │ ├── lib_traits.rs │ ├── main.rs │ └── main_traits.rs ├── 17-patterns-matching ├── Cargo.toml └── src │ ├── main.rs │ ├── pattern_syntax.rs │ ├── patterns.rs │ └── refutability.rs └── 18-advanced-features ├── Cargo.toml ├── hello_macro ├── Cargo.toml ├── hello_macro_derive │ ├── Cargo.toml │ └── src │ │ └── lib.rs └── src │ ├── lib.rs │ └── main.rs ├── pancakes ├── Cargo.toml └── src │ └── main.rs └── src ├── advanced_functions.rs ├── advanced_traits.rs ├── advanced_types.rs ├── main.rs └── unsafe_rust.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated files 2 | target/ 3 | 4 | # The Cargo.lock file should be included in the repository if you're writing an executable, and should be ignored if you're writing a library. 5 | Cargo.lock -------------------------------------------------------------------------------- /01-hello-cargo/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-cargo" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | # crates 10 | -------------------------------------------------------------------------------- /01-hello-cargo/README.md: -------------------------------------------------------------------------------- 1 | # Hello Cargo 2 | 3 | A quick intro to Rust syntax. 4 | 5 | ## Features 6 | 7 | - installing Rustup, [Rust for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust) and [Even Better TOML](https://marketplace.visualstudio.com/items?itemName=tamasfe.even-better-toml). 8 | - setting up a project with Cargo. 9 | - printing and formatting strings. 10 | - handling variables. 11 | - discovering primitive types. 12 | - playing with strings, tuples, arrays and vectors. 13 | - rendering conditionals. 14 | - using loops, functions and pointer references. 15 | - creating custom data types with structs. 16 | - defining enums. 17 | - grabbing args from CLI. 18 | 19 | Based on [Rust Crash Course](https://www.youtube.com/watch?v=zF34dRivLOw) by Brad Traversy (2019). -------------------------------------------------------------------------------- /01-hello-cargo/commands.md: -------------------------------------------------------------------------------- 1 | # Useful Commands 2 | 3 | - cargo new projectname 4 | - cargo init 5 | - cargo run 6 | - cargo check: to make sure the program compiles 7 | - ./target/debug/hello-cargo 8 | - cargo build 9 | - cargo build --release -------------------------------------------------------------------------------- /01-hello-cargo/src/arrays.rs: -------------------------------------------------------------------------------- 1 | // Arrays are fixed lists where elements are the same data type 2 | use std::mem; 3 | 4 | pub fn run() { 5 | let mut numbers: [i32; 5] = [1, 2, 3, 4, 5]; 6 | // Re-assign value 7 | numbers[2] = 20; 8 | println!("{:?}", numbers); 9 | // Get single value 10 | println!("Single value: {}", numbers[0]); // 1 11 | // Get array length 12 | println!("Array length: {}", numbers.len()); // 5 13 | // Arrays are stack allocated 14 | // println!("Array occupies {} bytes", std::mem::size_of_val(&numbers)); // 24 15 | println!("Array occupies {} bytes", mem::size_of_val(&numbers)); // 24 16 | // Get Slice 17 | let slice: &[i32] = &numbers[0..2]; 18 | println!("Slice: {:?}", slice); // [1, 2] 19 | } -------------------------------------------------------------------------------- /01-hello-cargo/src/cli.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | // cargo run hello 4 | 5 | pub fn run() { 6 | let args: Vec = env::args().collect(); 7 | let command = args[1].clone(); // args[0] = path 8 | let name = "Brad"; 9 | let status = "100%"; 10 | // println!("Command: {:?}", command); 11 | if command == "hello" { 12 | println!("Hi {}, how are you?", name); 13 | } else if command == "status" { 14 | println!("Status is {}", status); 15 | } else { 16 | println!("That is not a valid command"); 17 | } 18 | } -------------------------------------------------------------------------------- /01-hello-cargo/src/conditionals.rs: -------------------------------------------------------------------------------- 1 | pub fn run() { 2 | let age: i8 = 18; 3 | let check_id: bool = true; 4 | let knows_person_of_age: bool = true; 5 | 6 | // if/else 7 | if age >= 21 && check_id || knows_person_of_age { 8 | println!("Bartender: What would you like to drink?"); 9 | } else if age < 21 && check_id { 10 | println!("Bartender: Sorry, you have to leave"); 11 | } else { 12 | println!("Bartender: I'll need to see your ID"); 13 | } 14 | 15 | // shorthand if 16 | let is_of_age = if age >= 21 { true } else { false }; 17 | println!("Is Of Age: {}", is_of_age); 18 | } -------------------------------------------------------------------------------- /01-hello-cargo/src/enums.rs: -------------------------------------------------------------------------------- 1 | enum Movement { 2 | // Variants 3 | Up, 4 | Down, 5 | Left, 6 | Right 7 | } 8 | 9 | fn move_avatar(m: Movement) { 10 | // Perform action depending on info 11 | match m { 12 | Movement::Up => println!("Avatar moving Up"), 13 | Movement::Down => println!("Avatar moving Down"), 14 | Movement::Left => println!("Avatar moving Left"), 15 | Movement::Right => println!("Avatar moving Right") 16 | } 17 | } 18 | 19 | pub fn run() { 20 | let avatar1 = Movement::Left; 21 | let avatar2 = Movement::Up; 22 | let avatar3 = Movement::Right; 23 | let avatar4 = Movement::Down; 24 | move_avatar(avatar1); 25 | move_avatar(avatar2); 26 | move_avatar(avatar3); 27 | move_avatar(avatar4); 28 | } -------------------------------------------------------------------------------- /01-hello-cargo/src/functions.rs: -------------------------------------------------------------------------------- 1 | pub fn run() { 2 | greeting("Hello", "Brad"); 3 | // Bind function values to variables 4 | let get_sum = add(5, 5); 5 | println!("Sum: {}", get_sum); 6 | // Closure 7 | let n3: i32 = 10; 8 | let add_nums = |n1: i32, n2: i32| n1 + n2 + n3; 9 | println!("C Sum: {}", add_nums(3, 3)); // 16 10 | } 11 | 12 | fn greeting(greet: &str, name: &str) { 13 | println!("{} {}, nice to meet you!", greet, name); 14 | } 15 | 16 | fn add(n1: i32, n2: i32) -> i32 { 17 | n1 + n2 18 | } -------------------------------------------------------------------------------- /01-hello-cargo/src/loops.rs: -------------------------------------------------------------------------------- 1 | pub fn run() { 2 | // let mut count = 0; 3 | 4 | // Infinite loop 5 | // loop { 6 | // count += 1; 7 | // println!("Number: {}", count); 8 | 9 | // if count == 20 { 10 | // break; 11 | // } 12 | // } 13 | 14 | // While loop (FizzBuzz) 15 | // while count <= 100 { 16 | // if count % 15 == 0 { 17 | // println!("fizzbuzz"); 18 | // } else if count % 3 == 0 { 19 | // println!("fizz"); 20 | // } else if count % 5 == 0 { 21 | // println!("buzz"); 22 | // } else { 23 | // println!("{}", count); 24 | // } 25 | // count += 1; 26 | // } 27 | 28 | // For range 29 | for x in 0..100 { 30 | if x % 15 == 0 { 31 | println!("fizzbuzz"); 32 | } else if x % 3 == 0 { 33 | println!("fizz"); 34 | } else if x % 5 == 0 { 35 | println!("buzz"); 36 | } else { 37 | println!("{}", x); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /01-hello-cargo/src/main.rs: -------------------------------------------------------------------------------- 1 | // mod print; 2 | // mod vars; 3 | // mod types; 4 | // mod strings; 5 | // mod tuples; 6 | // mod arrays; 7 | // mod vectors; 8 | // mod conditionals; 9 | // mod loops; 10 | // mod functions; 11 | // mod pointer_ref; 12 | // mod structs; 13 | // mod enums; 14 | mod cli; 15 | 16 | fn main() { 17 | // println!("Hello World!") 18 | // print::run() 19 | // vars::run() 20 | // types::run() 21 | // strings::run() 22 | // tuples::run() 23 | // arrays::run() 24 | // vectors::run() 25 | // conditionals::run() 26 | // loops::run() 27 | // functions::run() 28 | // pointer_ref::run() 29 | // structs::run() 30 | // enums::run() 31 | cli::run() 32 | } 33 | -------------------------------------------------------------------------------- /01-hello-cargo/src/pointer_ref.rs: -------------------------------------------------------------------------------- 1 | pub fn run() { 2 | // Primitive array 3 | // let arr1 = [1, 2, 3]; 4 | // let arr2 = arr1; 5 | // println!("Values: {:?}", (arr1, arr2)); // [1, 2, 3], [1, 2, 3] 6 | // Vector 7 | // Non primitives: & is necessary to point to the resource (if re-assigned, the first variable is no longer holding the value by default) 8 | let vec1 = vec![1, 2, 3]; 9 | let vec2 = &vec1; 10 | println!("Values: {:?}", (&vec1, vec2)); 11 | } -------------------------------------------------------------------------------- /01-hello-cargo/src/print.rs: -------------------------------------------------------------------------------- 1 | pub fn run() { 2 | // Print to console 3 | println!("Hello from the print.rs file"); 4 | // Basic formatting 5 | println!("Number: {}", 1); 6 | println!("{} is from {}", "Brad", "Mass"); 7 | // Positional arguments 8 | println!("{0} is from {1} and {0} likes to {2}", "Brad", "Mass", "code"); 9 | // Named arguments 10 | println!("{name} likes to play {activity}", name = "John", activity= "Baseball"); 11 | // Placeholder traits 12 | println!("Binary: {:b} Hex: {:x} Octal: {:o}", 10, 10, 10); // 1010, a, 12 13 | // Placeholder for debug trait 14 | println!("{:?}", (12, true, "hello")); 15 | // Basic math 16 | println!("10 + 10 = {}", 10 + 10); 17 | } -------------------------------------------------------------------------------- /01-hello-cargo/src/strings.rs: -------------------------------------------------------------------------------- 1 | // Primitive str = immutable fixed-length string 2 | // String = growable 3 | 4 | pub fn run() { 5 | let mut hello = String::from("Hello "); 6 | // Get length 7 | println!("Length: {}", hello.len()); 8 | // Add char 9 | hello.push('W'); 10 | // Add string 11 | hello.push_str("orld!"); 12 | println!("{}", hello); 13 | // Get capacity in bytes 14 | println!("Capacity: {}", hello.capacity()); 15 | // Check if empty 16 | println!("Is empty: {}", hello.is_empty()); // false 17 | // Contains 18 | println!("Contains 'World': {}", hello.contains("World")); // true 19 | // Replace 20 | println!("Replace: {}", hello.replace("World", "There")); // Hello There 21 | // Loop through string by whitespace 22 | for word in hello.split_whitespace() { 23 | println!("{}", word); 24 | } 25 | // Create string with capacity 26 | let mut s = String::with_capacity(10); 27 | s.push('a'); 28 | s.push('b'); 29 | println!("{}", s); 30 | // Assertion testing 31 | assert_eq!(2, s.len()); 32 | assert_eq!(10, s.capacity()); 33 | } -------------------------------------------------------------------------------- /01-hello-cargo/src/structs.rs: -------------------------------------------------------------------------------- 1 | // Traditional struct 2 | // struct Color { 3 | // red: u8, 4 | // green: u8, 5 | // blue: u8 6 | // } 7 | 8 | // Tuple struct 9 | // struct Color(u8, u8, u8); 10 | 11 | struct Person { 12 | first_name: String, 13 | last_name: String 14 | } 15 | 16 | impl Person { 17 | // Construct person 18 | fn new(first: &str, last: &str) -> Person { 19 | Person { 20 | first_name: first.to_string(), 21 | last_name: last.to_string() 22 | } 23 | } 24 | 25 | // Get full name 26 | fn full_name(&self) -> String { 27 | format!("{} {}", self.first_name, self.last_name) // format without printing 28 | } 29 | 30 | // Set last name 31 | fn set_last_name(&mut self, last: &str) { 32 | self.last_name = last.to_string(); 33 | } 34 | 35 | // Name to tuple 36 | fn to_tuple(self) -> (String, String) { 37 | (self.first_name, self.last_name) 38 | } 39 | } 40 | 41 | pub fn run() { 42 | // Traditional struct 43 | // let mut c = Color { 44 | // red: 255, 45 | // green: 0, 46 | // blue: 0 47 | // }; 48 | // c.red = 200; 49 | // println!("Color: {} {} {}", c.red, c.green, c.blue); 50 | 51 | // Tuple struct 52 | // let mut c = Color(255, 0, 0); 53 | // c.0 = 200; 54 | // println!("Color: {} {} {}", c.0, c.1, c.2); 55 | 56 | let mut p = Person::new("John", "Doe"); 57 | // println!("Person: {} {}", p.first_name, p.last_name); 58 | // println!("Person: {}", p.full_name()); 59 | p.set_last_name("Williams"); 60 | println!("Person: {}", p.full_name()); 61 | println!("Person tuple: {:?}", p.to_tuple()); 62 | } -------------------------------------------------------------------------------- /01-hello-cargo/src/tuples.rs: -------------------------------------------------------------------------------- 1 | // Tuples group together values of different types 2 | // up to 12 elements 3 | 4 | pub fn run() { 5 | let person: (&str, &str, i8) = ("Brad", "Mass", 37); 6 | println!("{} is from {} and is {}", person.0, person.1, person.2); 7 | } -------------------------------------------------------------------------------- /01-hello-cargo/src/types.rs: -------------------------------------------------------------------------------- 1 | // Primitive types 2 | // Integers: u8, i8, u16, i16, u32, i32, u64, i64, u128, i128 (unsigned: no negative values) 3 | // Floats: f32, f64 4 | // Boolean (bool) 5 | // Characters (char) 6 | // Tuples 7 | // Arrays 8 | 9 | // Rust is a statically typed language, but it can infer what type we want to use (it is not required to set the type). 10 | 11 | pub fn run() { 12 | // Default is "i32" 13 | let x = 1; 14 | // Default is "f64" 15 | let y = 2.5; 16 | // Add explicit type 17 | let z: i64 = 4545445454545; 18 | // Find max size 19 | println!("Max i32: {}", std::i32::MAX); // 2147483647 20 | println!("Max i64: {}", std::i64::MAX); // 9223372036854775807 21 | // Boolean 22 | // let is_active = true; 23 | let is_active: bool = true; 24 | // Get boolean from expression 25 | let is_greater: bool = 10 > 5; // true 26 | // Char with single quotes 27 | let a1 = 'a'; 28 | let face = '\u{1F600}'; // emoji unicode: https://unicode.org/emoji/charts/full-emoji-list.html 29 | println!("{:?}", (x, y, z, is_active, is_greater, a1, face)); 30 | } -------------------------------------------------------------------------------- /01-hello-cargo/src/vars.rs: -------------------------------------------------------------------------------- 1 | // Variables hold primitive data or references to data 2 | // Variables are immutable by default 3 | // Rust is a block-scoped language 4 | 5 | pub fn run() { 6 | let name = "Brad"; 7 | let mut age = 37; 8 | println!("My name is {} and I am {}", name, age); 9 | age = 38; 10 | println!("My name is {} and I am {}", name, age); 11 | // Define constant 12 | const ID: i32 = 001; 13 | println!("ID: {}", ID); 14 | // Assign multiple vars 15 | let (my_name, my_age) = ("Brad", 37); 16 | println!("{} is {}", my_name, my_age); 17 | } -------------------------------------------------------------------------------- /01-hello-cargo/src/vectors.rs: -------------------------------------------------------------------------------- 1 | // Vectors are resizable arrays 2 | use std::mem; 3 | 4 | pub fn run() { 5 | let mut numbers: Vec = vec![1, 2, 3, 4, 5]; 6 | // Re-assign value 7 | numbers[2] = 20; 8 | // Add on to vector 9 | numbers.push(6); 10 | println!("{:?}", numbers); 11 | // Pop off last value 12 | numbers.pop(); 13 | println!("{:?}", numbers); 14 | // Get single value 15 | println!("Single value: {}", numbers[0]); // 1 16 | // Get vector length 17 | println!("Vector length: {}", numbers.len()); // 5 18 | // vectors are stack allocated 19 | println!("Vector occupies {} bytes", mem::size_of_val(&numbers)); // 24 20 | // Get Slice 21 | let slice: &[i32] = &numbers[0..2]; 22 | println!("Slice: {:?}", slice); // [1, 2] 23 | // Loop through vector values 24 | for x in numbers.iter() { 25 | println!("Number: {}", x); 26 | } 27 | // Loop & mutate values 28 | for x in numbers.iter_mut() { 29 | *x *= 2; 30 | } 31 | println!("Numbers Vec: {:?}", numbers); // [2, 4, 40, 8, 10] 32 | } -------------------------------------------------------------------------------- /02-guessing-game/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "guessing_game" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | rand = "0.8.3" 10 | -------------------------------------------------------------------------------- /02-guessing-game/README.md: -------------------------------------------------------------------------------- 1 | # Guessing Game 2 | 3 | A hands-on introduction to the Rust language. 4 | 5 | ## Features 6 | 7 | - handling user input with the io library. 8 | - adding rand from [crates.io](https://crates.io/) as a dependency. 9 | - generating a secret number. 10 | - comparing the guess to the secret number. 11 | - allowing multiple guesses with looping. 12 | - quitting after a correct guess. 13 | - handling invalid input. 14 | 15 | Based on [The Rust Programming Language](https://doc.rust-lang.org/book/) by Steve Klabnik and Carol Nichols (2021). -------------------------------------------------------------------------------- /02-guessing-game/src/main.rs: -------------------------------------------------------------------------------- 1 | use rand::Rng; 2 | use std::cmp::Ordering; 3 | use std::io; 4 | 5 | pub struct Guess { 6 | value: i32, 7 | } 8 | 9 | impl Guess { 10 | pub fn new(value: i32) -> Guess { 11 | if value < 1 || value > 100 { 12 | panic!( 13 | "The secret number will be between 1 and 100, got {}.", 14 | value 15 | ); 16 | } 17 | Guess { value } 18 | } 19 | 20 | pub fn value(&self) -> i32 { 21 | self.value 22 | } 23 | } 24 | 25 | fn main() { 26 | println!("Guess the number!"); 27 | let secret_number = rand::thread_rng().gen_range(1..101); 28 | // println!("The secret number is {}", secret_number); 29 | loop { 30 | println!("Please input your guess."); 31 | let mut guess = String::new(); 32 | io::stdin() 33 | .read_line(&mut guess) 34 | .expect("Failed to read line"); 35 | let guess: i32 = match guess.trim().parse() { 36 | // Result is an enum that has the variants Ok or Err 37 | Ok(num) => num, 38 | Err(_) => continue, // ask for another guess 39 | }; 40 | // error handling 41 | Guess::new(guess); 42 | println!("You guessed: {}", guess); 43 | match guess.cmp(&secret_number) { 44 | Ordering::Less => println!("Too small!"), 45 | Ordering::Greater => println!("Too big"), 46 | Ordering::Equal => { 47 | println!("You win!"); 48 | break; 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /03-minigrep/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "minigrep" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /03-minigrep/README.md: -------------------------------------------------------------------------------- 1 | # Minigrep 2 | 3 | A simple version of the classic command line tool grep. 4 | 5 | ## Features 6 | 7 | - accepting command line arguments. 8 | - reading a file. 9 | - refactoring to improve modularity and error handling. 10 | - developing the library’s functionality with Test-Driven Development (TDD). 11 | - working with environment variables. 12 | - writing error messages to standard error instead of standard output. 13 | - using closures and iterators. 14 | 15 | Based on [The Rust Programming Language](https://doc.rust-lang.org/book/) by Steve Klabnik and Carol Nichols (2021). -------------------------------------------------------------------------------- /03-minigrep/output.txt: -------------------------------------------------------------------------------- 1 | Are you nobody, too? 2 | How dreary to be somebody! 3 | -------------------------------------------------------------------------------- /03-minigrep/poem.txt: -------------------------------------------------------------------------------- 1 | I'm nobody! Who are you? 2 | Are you nobody, too? 3 | Then there's a pair of us - don't tell! 4 | They'd banish us, you know. 5 | 6 | How dreary to be somebody! 7 | How public, like a frog 8 | To tell your name the livelong day 9 | To an admiring bog! -------------------------------------------------------------------------------- /03-minigrep/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::error::Error; 3 | use std::fs; 4 | 5 | pub struct Config { 6 | pub query: String, 7 | pub filename: String, 8 | pub case_sensitive: bool, 9 | } 10 | 11 | impl Config { 12 | // pub fn new(args: &[String]) -> Result { 13 | // if args.len() < 3 { 14 | // return Err("not enough arguments"); 15 | // } 16 | // let query = args[1].clone(); 17 | // let filename = args[2].clone(); 18 | 19 | // let case_sensitive = env::var("CASE_INSENSITIVE").is_err(); // false if CASE_INSENSITIVE is set 20 | 21 | // Ok(Config { 22 | // query, 23 | // filename, 24 | // case_sensitive, 25 | // }) 26 | // } 27 | 28 | pub fn new(mut args: env::Args) -> Result { 29 | args.next(); // skip the name of the program 30 | let query = match args.next() { 31 | Some(arg) => arg, 32 | None => return Err("Didn't get a query string"), 33 | }; 34 | let filename = match args.next() { 35 | Some(arg) => arg, 36 | None => return Err("Didn't get a file name"), 37 | }; 38 | 39 | let case_sensitive = env::var("CASE_INSENSITIVE").is_err(); // false if CASE_INSENSITIVE is set 40 | 41 | Ok(Config { 42 | query, 43 | filename, 44 | case_sensitive, 45 | }) 46 | } 47 | } 48 | 49 | pub fn run(config: Config) -> Result<(), Box> { 50 | let contents = fs::read_to_string(config.filename)?; 51 | // println!("With text:\n{}", contents); 52 | let results = if config.case_sensitive { 53 | search(&config.query, &contents) 54 | } else { 55 | search_case_insensitive(&config.query, &contents) 56 | }; 57 | for line in results { 58 | println!("{}", line); 59 | } 60 | Ok(()) 61 | } 62 | 63 | // pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> { 64 | // let mut results = Vec::new(); 65 | // for line in contents.lines() { 66 | // if line.contains(query) { 67 | // results.push(line); 68 | // } 69 | // } 70 | // results 71 | // } 72 | // Rust can’t possibly know which of the two arguments we need, so we need to tell it. 73 | // Because contents is the argument that contains all of our text and we want to return the parts of that text that match, 74 | // we know contents is the argument that should be connected to the return value using the lifetime syntax. 75 | 76 | pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> { 77 | contents 78 | .lines() 79 | .filter(|line| line.contains(query)) 80 | .collect() 81 | } 82 | 83 | pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> { 84 | let query = query.to_lowercase(); 85 | let mut results = Vec::new(); 86 | for line in contents.lines() { 87 | if line.to_lowercase().contains(&query) { 88 | results.push(line); 89 | } 90 | } 91 | results 92 | } 93 | // CASE_INSENSITIVE=1 cargo run to poem.txt 94 | 95 | #[cfg(test)] 96 | mod tests { 97 | use super::*; 98 | 99 | #[test] 100 | fn case_sensitive() { 101 | let query = "duct"; 102 | let contents = "\ 103 | Rust: 104 | safe, fast, productive. 105 | Pick three. 106 | Duct tape."; 107 | assert_eq!(vec!["safe, fast, productive."], search(query, contents)); 108 | } 109 | 110 | #[test] 111 | fn case_unsensitive() { 112 | let query = "rUst"; 113 | let contents = "\ 114 | Rust: 115 | safe, fast, productive. 116 | Pick three. 117 | Trust me."; 118 | assert_eq!( 119 | vec!["Rust:", "Trust me."], 120 | search_case_insensitive(query, contents) 121 | ); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /03-minigrep/src/main.rs: -------------------------------------------------------------------------------- 1 | // cargo run to poem.txt > output.txt // redirect output to a file 2 | 3 | use std::env; 4 | use std::process; 5 | 6 | use minigrep::Config; 7 | 8 | fn main() { 9 | // let args: Vec = env::args().collect(); 10 | // println!("{:?}", args); // ["target\\debug\\minigrep.exe", "needle", "haystack"] 11 | // let query = &args[1]; 12 | // let filename = &args[2]; 13 | 14 | // if args.len() < 3 { 15 | // panic!("not enough arguments"); 16 | // } 17 | 18 | // let config = Config::new(&args).unwrap_or_else(|err| { 19 | let config = Config::new(env::args()).unwrap_or_else(|err| { 20 | eprintln!("Problem parsing arguments: {}", err); 21 | // Write error messages to standard error instead of standard output 22 | process::exit(1); 23 | }); 24 | // println!("Searching for {}", config.query); 25 | // println!("In file {}", config.filename); 26 | 27 | if let Err(e) = minigrep::run(config) { 28 | eprintln!("Application error: {}", e); 29 | process::exit(1); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /04-hello/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Hello! 8 | 9 | 10 |

Oops!

11 |

Sorry, I don't know what you're asking for.

12 | 13 | -------------------------------------------------------------------------------- /04-hello/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /04-hello/README.md: -------------------------------------------------------------------------------- 1 | # Hello 2 | 3 | A multithreaded web server that says “hello”. 4 | 5 | ## Features 6 | 7 | - listening to the TCP connection and reading the request. 8 | - writing a response and returning HTML. 9 | - validating the request and selectively responding. 10 | - simulating a slow request. 11 | - improving throughput with a thread pool. 12 | - handling graceful shutdown. 13 | 14 | Based on [The Rust Programming Language](https://doc.rust-lang.org/book/) by Steve Klabnik and Carol Nichols (2021). -------------------------------------------------------------------------------- /04-hello/hello.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Hello! 8 | 9 | 10 |

Hello!

11 |

Hi from Rust

12 | 13 | -------------------------------------------------------------------------------- /04-hello/src/bin/main.rs: -------------------------------------------------------------------------------- 1 | use std::fs; 2 | use std::io::prelude::*; 3 | use std::net::TcpListener; 4 | use std::net::TcpStream; 5 | use std::thread; 6 | use std::time::Duration; 7 | 8 | use hello::ThreadPool; 9 | 10 | fn main() { 11 | let listener = TcpListener::bind("127.0.0.1:7878").unwrap(); 12 | let pool = ThreadPool::new(4); 13 | 14 | for stream in listener.incoming().take(2) { 15 | let stream = stream.unwrap(); 16 | // handle_connection(stream); 17 | pool.execute(|| { 18 | handle_connection(stream); 19 | }); 20 | } 21 | println!("Shutting down."); 22 | } 23 | 24 | fn handle_connection(mut stream: TcpStream) { 25 | let mut buffer = [0; 1024]; // declare a buffer on the stack to hold the data that is read in 26 | stream.read(&mut buffer).unwrap(); 27 | // println!("Request: {}", String::from_utf8_lossy(&buffer[..])); 28 | // let response = "HTTP/1.1 200 OK\r\n\r\n"; 29 | 30 | let get = b"GET / HTTP/1.1\r\n"; // byte string 31 | let sleep = b"GET /sleep HTTP/1.1\r\n"; 32 | 33 | let (status_line, filename) = if buffer.starts_with(get) { 34 | ("HTTP/1.1 200 OK", "hello.html") 35 | } else if buffer.starts_with(sleep) { 36 | // simulate a slow request 37 | thread::sleep(Duration::from_secs(5)); 38 | ("HTTP/1.1 200 OK", "hello.html") 39 | } else { 40 | ("HTTP/1.1 404 NOT FOUND", "404.html") 41 | }; 42 | let contents = fs::read_to_string(filename).unwrap(); 43 | let response = format!( 44 | "{}\r\nContent-Length: {}\r\n\r\n{}", 45 | status_line, 46 | contents.len(), 47 | contents 48 | ); 49 | stream.write(response.as_bytes()).unwrap(); 50 | stream.flush().unwrap(); 51 | } 52 | -------------------------------------------------------------------------------- /04-hello/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::sync::mpsc; 2 | use std::sync::Arc; 3 | use std::sync::Mutex; 4 | use std::thread; 5 | 6 | pub struct ThreadPool { 7 | workers: Vec, 8 | sender: mpsc::Sender, 9 | } 10 | 11 | type Job = Box; 12 | 13 | impl ThreadPool { 14 | /// Create a new ThreadPool. 15 | /// 16 | /// The size is the number of threads in the pool. 17 | /// 18 | /// # Panics 19 | /// 20 | /// The `new` function will panic if the size is zero. 21 | // cargo doc --open 22 | pub fn new(size: usize) -> ThreadPool { 23 | assert!(size > 0); 24 | let (sender, receiver) = mpsc::channel(); 25 | let receiver = Arc::new(Mutex::new(receiver)); 26 | let mut workers = Vec::with_capacity(size); 27 | for id in 0..size { 28 | // the workers can share ownership of the receiving end 29 | workers.push(Worker::new(id, Arc::clone(&receiver))); 30 | } 31 | ThreadPool { workers, sender } 32 | } 33 | 34 | pub fn execute(&self, f: F) 35 | where 36 | F: FnOnce() + Send + 'static, 37 | { 38 | let job = Box::new(f); 39 | self.sender.send(Message::NewJob(job)).unwrap(); 40 | } 41 | } 42 | 43 | struct Worker { 44 | id: usize, 45 | thread: Option>, 46 | } 47 | 48 | impl Worker { 49 | fn new(id: usize, receiver: Arc>>) -> Worker { 50 | let thread = thread::spawn(move || loop { 51 | let message = receiver.lock().unwrap().recv().unwrap(); 52 | match message { 53 | Message::NewJob(job) => { 54 | println!("Worker {} got a job; executing.", id); 55 | job(); 56 | } 57 | Message::Terminate => { 58 | println!("Worker {} was told to terminate.", id); 59 | break; 60 | } 61 | } 62 | }); 63 | Worker { 64 | id, 65 | thread: Some(thread), 66 | } 67 | } 68 | } 69 | 70 | impl Drop for ThreadPool { 71 | fn drop(&mut self) { 72 | println!("Sending terminate message to all workers."); 73 | 74 | for _ in &self.workers { 75 | self.sender.send(Message::Terminate).unwrap(); 76 | } 77 | 78 | for worker in &mut self.workers { 79 | println!("Shutting down worker {}", worker.id); 80 | if let Some(thread) = worker.thread.take() { 81 | // takes the Some variant out and leaves None in its place. 82 | thread.join().unwrap(); 83 | } 84 | } 85 | } 86 | } 87 | 88 | enum Message { 89 | NewJob(Job), 90 | Terminate, 91 | } 92 | -------------------------------------------------------------------------------- /05-game-of-life/.appveyor.yml: -------------------------------------------------------------------------------- 1 | install: 2 | - appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe 3 | - if not defined RUSTFLAGS rustup-init.exe -y --default-host x86_64-pc-windows-msvc --default-toolchain nightly 4 | - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin 5 | - rustc -V 6 | - cargo -V 7 | 8 | build: false 9 | 10 | test_script: 11 | - cargo test --locked 12 | -------------------------------------------------------------------------------- /05-game-of-life/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | bin/ 5 | pkg/ 6 | wasm-pack.log 7 | -------------------------------------------------------------------------------- /05-game-of-life/.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | sudo: false 3 | 4 | cache: cargo 5 | 6 | matrix: 7 | include: 8 | 9 | # Builds with wasm-pack. 10 | - rust: beta 11 | env: RUST_BACKTRACE=1 12 | addons: 13 | firefox: latest 14 | chrome: stable 15 | before_script: 16 | - (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update) 17 | - (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate) 18 | - cargo install-update -a 19 | - curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -f 20 | script: 21 | - cargo generate --git . --name testing 22 | # Having a broken Cargo.toml (in that it has curlies in fields) anywhere 23 | # in any of our parent dirs is problematic. 24 | - mv Cargo.toml Cargo.toml.tmpl 25 | - cd testing 26 | - wasm-pack build 27 | - wasm-pack test --chrome --firefox --headless 28 | 29 | # Builds on nightly. 30 | - rust: nightly 31 | env: RUST_BACKTRACE=1 32 | before_script: 33 | - (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update) 34 | - (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate) 35 | - cargo install-update -a 36 | - rustup target add wasm32-unknown-unknown 37 | script: 38 | - cargo generate --git . --name testing 39 | - mv Cargo.toml Cargo.toml.tmpl 40 | - cd testing 41 | - cargo check 42 | - cargo check --target wasm32-unknown-unknown 43 | - cargo check --no-default-features 44 | - cargo check --target wasm32-unknown-unknown --no-default-features 45 | - cargo check --no-default-features --features console_error_panic_hook 46 | - cargo check --target wasm32-unknown-unknown --no-default-features --features console_error_panic_hook 47 | - cargo check --no-default-features --features "console_error_panic_hook wee_alloc" 48 | - cargo check --target wasm32-unknown-unknown --no-default-features --features "console_error_panic_hook wee_alloc" 49 | 50 | # Builds on beta. 51 | - rust: beta 52 | env: RUST_BACKTRACE=1 53 | before_script: 54 | - (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update) 55 | - (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate) 56 | - cargo install-update -a 57 | - rustup target add wasm32-unknown-unknown 58 | script: 59 | - cargo generate --git . --name testing 60 | - mv Cargo.toml Cargo.toml.tmpl 61 | - cd testing 62 | - cargo check 63 | - cargo check --target wasm32-unknown-unknown 64 | - cargo check --no-default-features 65 | - cargo check --target wasm32-unknown-unknown --no-default-features 66 | - cargo check --no-default-features --features console_error_panic_hook 67 | - cargo check --target wasm32-unknown-unknown --no-default-features --features console_error_panic_hook 68 | # Note: no enabling the `wee_alloc` feature here because it requires 69 | # nightly for now. 70 | -------------------------------------------------------------------------------- /05-game-of-life/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "game-of-life" 3 | version = "0.1.0" 4 | authors = ["solygambas"] 5 | edition = "2021" 6 | description = "A zero-player game to learn how to use Rust, WebAssembly, and JavaScript together." 7 | repository = "https://github.com/solygambas/rust-projects" 8 | license = "MIT OR Apache-2.0" 9 | 10 | [lib] 11 | crate-type = ["cdylib", "rlib"] 12 | 13 | [features] 14 | default = ["console_error_panic_hook"] 15 | 16 | [dependencies] 17 | wasm-bindgen = "0.2.63" 18 | # js-sys = "0.3" 19 | 20 | # The `console_error_panic_hook` crate provides better debugging of panics by 21 | # logging them with `console.error`. This is great for development, but requires 22 | # all the `std::fmt` and `std::panicking` infrastructure, so isn't great for 23 | # code size when deploying. 24 | console_error_panic_hook = { version = "0.1.6", optional = true } 25 | 26 | # `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size 27 | # compared to the default allocator's ~10K. It is slower than the default 28 | # allocator, however. 29 | # 30 | # Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now. 31 | wee_alloc = { version = "0.4.5", optional = true } 32 | 33 | [dev-dependencies] 34 | wasm-bindgen-test = "0.3.13" 35 | 36 | # logging 37 | [dependencies.web-sys] 38 | version = "0.3" 39 | features = [ 40 | "console", 41 | ] 42 | 43 | [profile.release] 44 | # Tell `rustc` to optimize for small code size. 45 | # opt-level = "s" # 37143 46 | lto = true 47 | opt-level = "z" # wasm-opt -Oz # https://rustwasm.github.io/book/reference/code-size.html#use-the-wasm-opt-tool 48 | # gzip -9 < pkg/game_of_life_bg.wasm | wc -c 49 | 50 | # publish 51 | # wasm-pack login 52 | # wasm-pack publish -------------------------------------------------------------------------------- /05-game-of-life/LICENSE_APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /05-game-of-life/LICENSE_MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 solygambas <51904909+solygambas@users.noreply.github.com> 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /05-game-of-life/README.md: -------------------------------------------------------------------------------- 1 | # Game of Life 2 | 3 | A zero-player game to learn how to use Rust, WebAssembly, and JavaScript together. 4 | 5 | 6 | 7 | ## Features 8 | 9 | - cloning the project template and understanding the folder structure. 10 | - building the project with wasm-pack. 11 | - putting it into a web page with wasm-app. 12 | - serving locally with webpack. 13 | - implementing the Game of Life with Rust. 14 | - rendering to canvas directly from memory with JavaScript. 15 | - testing the tick function and debugging. 16 | - pausing and resuming the game. 17 | - toggling a cell's state on click. 18 | - optimizing performances with time profiling and benchmarking. 19 | - shrinking .wasm size with wasm-opt and gzip. 20 | 21 | Based on [The Rust and WebAssembly Book](https://rustwasm.github.io/docs/book/) by The Rust and WebAssembly Working Group (2021). 22 | -------------------------------------------------------------------------------- /05-game-of-life/after.txt: -------------------------------------------------------------------------------- 1 | 2 | running 0 tests 3 | 4 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s 5 | 6 | 7 | running 1 test 8 | test universe_ticks ... bench: 178,561 ns/iter (+/- 14,685) 9 | 10 | test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in 3.32s 11 | 12 | -------------------------------------------------------------------------------- /05-game-of-life/before.txt: -------------------------------------------------------------------------------- 1 | 2 | running 0 tests 3 | 4 | test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s 5 | 6 | 7 | running 1 test 8 | test universe_ticks ... bench: 748,300 ns/iter (+/- 50,043) 9 | 10 | test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out; finished in 4.16s 11 | 12 | -------------------------------------------------------------------------------- /05-game-of-life/benches/bench.rs: -------------------------------------------------------------------------------- 1 | // to use feature, you need to run: rustup default nightly 2 | // cargo bench | tee before.txt 3 | // cargo bench | tee after.txt 4 | // cargo benchcmp before.txt after.txt // x 4.19 5 | // rustup default stable 6 | #![feature(test)] 7 | 8 | extern crate game_of_life; 9 | extern crate test; 10 | 11 | #[bench] 12 | fn universe_ticks(b: &mut test::Bencher) { 13 | let mut universe = game_of_life::Universe::new(); 14 | 15 | b.iter(|| { 16 | universe.tick(); 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /05-game-of-life/commands.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |

wasm-pack-template

4 | 5 | A template for kick starting a Rust and WebAssembly project using wasm-pack. 6 | 7 |

8 | Build Status 9 |

10 | 11 |

12 | Tutorial 13 | | 14 | Chat 15 |

16 | 17 | Built with 🦀🕸 by The Rust and WebAssembly Working Group 18 |
19 | 20 | ## About 21 | 22 | [**📚 Read this template tutorial! 📚**][template-docs] 23 | 24 | This template is designed for compiling Rust libraries into WebAssembly and 25 | publishing the resulting package to NPM. 26 | 27 | Be sure to check out [other `wasm-pack` tutorials online][tutorials] for other 28 | templates and usages of `wasm-pack`. 29 | 30 | [tutorials]: https://rustwasm.github.io/docs/wasm-pack/tutorials/index.html 31 | [template-docs]: https://rustwasm.github.io/docs/wasm-pack/tutorials/npm-browser-packages/index.html 32 | 33 | ## 🚴 Usage 34 | 35 | ### 🐑 Use `cargo generate` to Clone this Template 36 | 37 | [Learn more about `cargo generate` here.](https://github.com/ashleygwilliams/cargo-generate) 38 | 39 | ``` 40 | cargo generate --git https://github.com/rustwasm/wasm-pack-template.git --name my-project 41 | cd my-project 42 | ``` 43 | 44 | ### 🛠️ Build with `wasm-pack build` 45 | 46 | ``` 47 | wasm-pack build 48 | ``` 49 | 50 | ### 🛠️ Add web page with `npm init wasm-app www` 51 | 52 | ``` 53 | npm init wasm-app www 54 | ``` 55 | 56 | ### 🔬 Test in Headless Browsers with `wasm-pack test` 57 | 58 | ``` 59 | wasm-pack test --headless --firefox 60 | ``` 61 | 62 | ### 🎁 Publish to NPM with `wasm-pack publish` 63 | 64 | ``` 65 | wasm-pack publish 66 | ``` 67 | 68 | ## 🔋 Batteries Included 69 | 70 | * [`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) for communicating 71 | between WebAssembly and JavaScript. 72 | * [`console_error_panic_hook`](https://github.com/rustwasm/console_error_panic_hook) 73 | for logging panic messages to the developer console. 74 | * [`wee_alloc`](https://github.com/rustwasm/wee_alloc), an allocator optimized 75 | for small code size. 76 | -------------------------------------------------------------------------------- /05-game-of-life/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solygambas/rust-projects/7b34af69709e62162549c74f7ac015e623a7649f/05-game-of-life/screenshot.png -------------------------------------------------------------------------------- /05-game-of-life/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod utils; 2 | 3 | use std::fmt; 4 | use wasm_bindgen::prelude::*; 5 | // extern crate js_sys; 6 | extern crate web_sys; 7 | use web_sys::console; 8 | 9 | // A macro to provide `println!(..)`-style syntax for `console.log` logging. 10 | // macro_rules! log { 11 | // ($( $t:tt)* ) => { 12 | // console::log_1(&format!( $( $t )*).into()); 13 | // }; 14 | // } 15 | 16 | pub struct Timer<'a> { 17 | name: &'a str, 18 | } 19 | 20 | impl<'a> Timer<'a> { 21 | pub fn new(name: &'a str) -> Timer<'a> { 22 | console::time_with_label(name); 23 | Timer { name } 24 | } 25 | } 26 | 27 | impl<'a> Drop for Timer<'a> { 28 | fn drop(&mut self) { 29 | console::time_end_with_label(self.name); 30 | } 31 | } 32 | 33 | #[wasm_bindgen] 34 | #[repr(u8)] 35 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 36 | pub enum Cell { 37 | Dead = 0, 38 | Alive = 1, 39 | } 40 | 41 | impl Cell { 42 | fn toggle(&mut self) { 43 | *self = match *self { 44 | Cell::Dead => Cell::Alive, 45 | Cell::Alive => Cell::Dead, 46 | } 47 | } 48 | } 49 | 50 | #[wasm_bindgen] 51 | pub struct Universe { 52 | width: u32, 53 | height: u32, 54 | cells: Vec, 55 | } 56 | 57 | impl Universe { 58 | fn get_index(&self, row: u32, column: u32) -> usize { 59 | (row * self.width + column) as usize 60 | } 61 | 62 | fn live_neighbor_count(&self, row: u32, column: u32) -> u8 { 63 | // let mut count = 0; 64 | // for delta_row in [self.height - 1, 0, 1].iter().cloned() { 65 | // for delta_col in [self.width - 1, 0, 1].iter().cloned() { 66 | // if delta_row == 0 && delta_col == 0 { 67 | // continue; 68 | // } 69 | // let neighbor_row = (row + delta_row) % self.height; 70 | // let neighbor_col = (column + delta_col) % self.width; 71 | // let idx = self.get_index(neighbor_row, neighbor_col); 72 | // count += self.cells[idx] as u8; 73 | // } 74 | // } 75 | // count 76 | 77 | let mut count = 0; 78 | 79 | let north = if row == 0 { self.height - 1 } else { row - 1 }; 80 | 81 | let south = if row == self.height - 1 { 0 } else { row + 1 }; 82 | 83 | let west = if column == 0 { 84 | self.width - 1 85 | } else { 86 | column - 1 87 | }; 88 | 89 | let east = if column == self.width - 1 { 90 | 0 91 | } else { 92 | column + 1 93 | }; 94 | 95 | let nw = self.get_index(north, west); 96 | count += self.cells[nw] as u8; 97 | 98 | let n = self.get_index(north, column); 99 | count += self.cells[n] as u8; 100 | 101 | let ne = self.get_index(north, east); 102 | count += self.cells[ne] as u8; 103 | 104 | let w = self.get_index(row, west); 105 | count += self.cells[w] as u8; 106 | 107 | let e = self.get_index(row, east); 108 | count += self.cells[e] as u8; 109 | 110 | let sw = self.get_index(south, west); 111 | count += self.cells[sw] as u8; 112 | 113 | let s = self.get_index(south, column); 114 | count += self.cells[s] as u8; 115 | 116 | let se = self.get_index(south, east); 117 | count += self.cells[se] as u8; 118 | 119 | count 120 | } 121 | 122 | /// Get the dead and alive values of the entire universe. 123 | pub fn get_cells(&self) -> &[Cell] { 124 | &self.cells 125 | } 126 | /// Set cells to be alive in a universe by passing the row and column 127 | /// of each cell as an array. 128 | pub fn set_cells(&mut self, cells: &[(u32, u32)]) { 129 | for (row, col) in cells.iter().cloned() { 130 | let idx = self.get_index(row, col); 131 | self.cells[idx] = Cell::Alive; 132 | } 133 | } 134 | } 135 | 136 | /// Public methods, exported to JavaScript. 137 | #[wasm_bindgen] 138 | impl Universe { 139 | pub fn tick(&mut self) { 140 | let _timer = Timer::new("Universe::tick"); 141 | let mut next = { 142 | let _timer = Timer::new("allocate next cells"); // allocate next cells: 0.046875 ms 143 | self.cells.clone() 144 | }; 145 | 146 | { 147 | let _timer = Timer::new("new generation"); // new generation: 0.8623046875 ms 148 | // the vast majority of time is spent actually calculating the next generation of cells. 149 | for row in 0..self.height { 150 | for col in 0..self.width { 151 | let idx = self.get_index(row, col); 152 | let cell = self.cells[idx]; 153 | let live_neighbors = self.live_neighbor_count(row, col); 154 | 155 | // log!( 156 | // "cell[{}, {}] is initially {:?} and has {} live neighbors", 157 | // row, 158 | // col, 159 | // cell, 160 | // live_neighbors 161 | // ); 162 | 163 | let next_cell = match (cell, live_neighbors) { 164 | // Rule 1: Any live cell with fewer than two live neighbours 165 | // dies, as if caused by underpopulation. 166 | (Cell::Alive, x) if x < 2 => Cell::Dead, 167 | // Rule 2: Any live cell with two or three live neighbours 168 | // lives on to the next generation. 169 | (Cell::Alive, 2) | (Cell::Alive, 3) => Cell::Alive, 170 | // Rule 3: Any live cell with more than three live 171 | // neighbours dies, as if by overpopulation. 172 | (Cell::Alive, x) if x > 3 => Cell::Dead, 173 | // Rule 4: Any dead cell with exactly three live neighbours 174 | // becomes a live cell, as if by reproduction. 175 | (Cell::Dead, 3) => Cell::Alive, 176 | // All other cells remain in the same state. 177 | (otherwise, _) => otherwise, 178 | }; 179 | 180 | // log!("it becomes {:?}", next_cell); 181 | 182 | next[idx] = next_cell; 183 | } 184 | } 185 | } 186 | let _timer = Timer::new("free old cells"); // free old cells: 0.041015625 ms 187 | self.cells = next; 188 | } 189 | 190 | pub fn new() -> Universe { 191 | utils::set_panic_hook(); 192 | let width = 64; 193 | let height = 64; 194 | 195 | let cells = (0..width * height) 196 | .map(|i| { 197 | // if js_sys::Math::random() < 0.5 { 198 | if i % 2 == 0 || i % 7 == 0 { 199 | Cell::Alive 200 | } else { 201 | Cell::Dead 202 | } 203 | }) 204 | .collect(); 205 | 206 | Universe { 207 | width, 208 | height, 209 | cells, 210 | } 211 | } 212 | 213 | pub fn render(&self) -> String { 214 | self.to_string() 215 | } 216 | 217 | pub fn width(&self) -> u32 { 218 | self.width 219 | } 220 | 221 | pub fn height(&self) -> u32 { 222 | self.height 223 | } 224 | 225 | pub fn cells(&self) -> *const Cell { 226 | self.cells.as_ptr() 227 | } 228 | 229 | /// Set the width of the universe. 230 | /// 231 | /// Resets all cells to the dead state. 232 | pub fn set_width(&mut self, width: u32) { 233 | self.width = width; 234 | self.cells = (0..width * self.height).map(|_i| Cell::Dead).collect(); 235 | } 236 | 237 | /// Set the height of the universe. 238 | /// 239 | /// Resets all cells to the dead state. 240 | pub fn set_height(&mut self, height: u32) { 241 | self.height = height; 242 | self.cells = (0..self.width * height).map(|_i| Cell::Dead).collect(); 243 | } 244 | 245 | pub fn toggle_cell(&mut self, row: u32, col: u32) { 246 | let idx = self.get_index(row, col); 247 | self.cells[idx].toggle(); 248 | } 249 | } 250 | 251 | impl fmt::Display for Universe { 252 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 253 | for line in self.cells.as_slice().chunks(self.width as usize) { 254 | for &cell in line { 255 | let symbol = if cell == Cell::Dead { '◻' } else { '◼' }; 256 | write!(f, "{}", symbol)?; 257 | } 258 | write!(f, "\n")?; 259 | } 260 | Ok(()) 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /05-game-of-life/src/utils.rs: -------------------------------------------------------------------------------- 1 | pub fn set_panic_hook() { 2 | // When the `console_error_panic_hook` feature is enabled, we can call the 3 | // `set_panic_hook` function at least once during initialization, and then 4 | // we will get better error messages if our code ever panics. 5 | // 6 | // For more details see 7 | // https://github.com/rustwasm/console_error_panic_hook#readme 8 | #[cfg(feature = "console_error_panic_hook")] 9 | console_error_panic_hook::set_once(); 10 | } 11 | -------------------------------------------------------------------------------- /05-game-of-life/tests/web.rs: -------------------------------------------------------------------------------- 1 | //! Test suite for the Web and headless browsers. 2 | // wasm-pack test --chrome --headless 3 | 4 | #![cfg(target_arch = "wasm32")] 5 | 6 | extern crate wasm_bindgen_test; 7 | use wasm_bindgen_test::*; 8 | 9 | extern crate game_of_life; 10 | use game_of_life::Universe; 11 | 12 | wasm_bindgen_test_configure!(run_in_browser); 13 | 14 | #[wasm_bindgen_test] 15 | fn pass() { 16 | assert_eq!(1 + 1, 2); 17 | } 18 | 19 | #[cfg(test)] 20 | pub fn input_spaceship() -> Universe { 21 | let mut universe = Universe::new(); 22 | universe.set_width(6); 23 | universe.set_height(6); 24 | universe.set_cells(&[(1, 2), (2, 3), (3, 1), (3, 2), (3, 3)]); 25 | universe 26 | } 27 | 28 | #[cfg(test)] 29 | pub fn expected_spaceship() -> Universe { 30 | let mut universe = Universe::new(); 31 | universe.set_width(6); 32 | universe.set_height(6); 33 | universe.set_cells(&[(2, 1), (2, 3), (3, 2), (3, 3), (4, 2)]); // expected position after 1 tick 34 | universe 35 | } 36 | 37 | #[wasm_bindgen_test] 38 | pub fn test_tick() { 39 | // Let's create a smaller Universe with a small spaceship to test! 40 | let mut input_universe = input_spaceship(); 41 | // This is what our spaceship should look like 42 | let expected_universe = expected_spaceship(); 43 | // after one tick in our universe. 44 | // Call `tick` and then see if the cells in the `Universe`s are the same. 45 | input_universe.tick(); 46 | assert_eq!(&input_universe.get_cells(), &expected_universe.get_cells()); 47 | } 48 | -------------------------------------------------------------------------------- /05-game-of-life/www/.bin/create-wasm-app.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const { spawn } = require("child_process"); 4 | const fs = require("fs"); 5 | 6 | let folderName = '.'; 7 | 8 | if (process.argv.length >= 3) { 9 | folderName = process.argv[2]; 10 | if (!fs.existsSync(folderName)) { 11 | fs.mkdirSync(folderName); 12 | } 13 | } 14 | 15 | const clone = spawn("git", ["clone", "https://github.com/rustwasm/create-wasm-app.git", folderName]); 16 | 17 | clone.on("close", code => { 18 | if (code !== 0) { 19 | console.error("cloning the template failed!") 20 | process.exit(code); 21 | } else { 22 | console.log("🦀 Rust + 🕸 Wasm = ❤"); 23 | } 24 | }); 25 | -------------------------------------------------------------------------------- /05-game-of-life/www/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | -------------------------------------------------------------------------------- /05-game-of-life/www/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: "10" 3 | 4 | script: 5 | - ./node_modules/.bin/webpack 6 | -------------------------------------------------------------------------------- /05-game-of-life/www/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /05-game-of-life/www/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) [year] [name] 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /05-game-of-life/www/README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |

create-wasm-app

4 | 5 | An npm init template for kick starting a project that uses NPM packages containing Rust-generated WebAssembly and bundles them with Webpack. 6 | 7 |

8 | Build Status 9 |

10 | 11 |

12 | Usage 13 | | 14 | Chat 15 |

16 | 17 | Built with 🦀🕸 by The Rust and WebAssembly Working Group 18 |
19 | 20 | ## About 21 | 22 | This template is designed for depending on NPM packages that contain 23 | Rust-generated WebAssembly and using them to create a Website. 24 | 25 | * Want to create an NPM package with Rust and WebAssembly? [Check out 26 | `wasm-pack-template`.](https://github.com/rustwasm/wasm-pack-template) 27 | * Want to make a monorepo-style Website without publishing to NPM? Check out 28 | [`rust-webpack-template`](https://github.com/rustwasm/rust-webpack-template) 29 | and/or 30 | [`rust-parcel-template`](https://github.com/rustwasm/rust-parcel-template). 31 | 32 | ## 🚴 Usage 33 | 34 | ``` 35 | npm init wasm-app 36 | ``` 37 | 38 | ## 🔋 Batteries Included 39 | 40 | - `.gitignore`: ignores `node_modules` 41 | - `LICENSE-APACHE` and `LICENSE-MIT`: most Rust projects are licensed this way, so these are included for you 42 | - `README.md`: the file you are reading now! 43 | - `index.html`: a bare bones html document that includes the webpack bundle 44 | - `index.js`: example js file with a comment showing how to import and use a wasm pkg 45 | - `package.json` and `package-lock.json`: 46 | - pulls in devDependencies for using webpack: 47 | - [`webpack`](https://www.npmjs.com/package/webpack) 48 | - [`webpack-cli`](https://www.npmjs.com/package/webpack-cli) 49 | - [`webpack-dev-server`](https://www.npmjs.com/package/webpack-dev-server) 50 | - defines a `start` script to run `webpack-dev-server` 51 | - `webpack.config.js`: configuration file for bundling your js with webpack 52 | 53 | ## License 54 | 55 | Licensed under either of 56 | 57 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 58 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 59 | 60 | at your option. 61 | 62 | ### Contribution 63 | 64 | Unless you explicitly state otherwise, any contribution intentionally 65 | submitted for inclusion in the work by you, as defined in the Apache-2.0 66 | license, shall be dual licensed as above, without any additional terms or 67 | conditions. 68 | -------------------------------------------------------------------------------- /05-game-of-life/www/bootstrap.js: -------------------------------------------------------------------------------- 1 | // A dependency graph that contains any wasm must all be imported 2 | // asynchronously. This `bootstrap.js` file does the single async import, so 3 | // that no one else needs to worry about it again. 4 | import("./index.js") 5 | .catch(e => console.error("Error importing `index.js`:", e)); 6 | -------------------------------------------------------------------------------- /05-game-of-life/www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Game of Life 6 | 23 | 24 | 25 | 29 | 30 |
31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /05-game-of-life/www/index.js: -------------------------------------------------------------------------------- 1 | import { memory } from "game-of-life/game_of_life_bg"; 2 | import { Universe, Cell } from "game-of-life"; 3 | 4 | // const pre = document.getElementById("game-of-life-canvas"); 5 | // const universe = Universe.new(); 6 | 7 | // const renderLoop = () => { 8 | // pre.textContent = universe.render(); 9 | // universe.tick(); 10 | 11 | // requestAnimationFrame(renderLoop); 12 | // }; 13 | 14 | // requestAnimationFrame(renderLoop); 15 | 16 | const CELL_SIZE = 5; // px 17 | const GRID_COLOR = "#ccc"; 18 | const DEAD_COLOR = "#fff"; 19 | const ALIVE_COLOR = "#000"; 20 | 21 | // Construct the universe, and get its width and height. 22 | const universe = Universe.new(); 23 | const width = universe.width(); 24 | const height = universe.height(); 25 | 26 | // Give the canvas room for all of our cells and a 1px border 27 | // around each of them. 28 | const canvas = document.getElementById("game-of-life-canvas"); 29 | canvas.height = (CELL_SIZE + 1) * height + 1; 30 | canvas.width = (CELL_SIZE + 1) * width + 1; 31 | 32 | const ctx = canvas.getContext("2d"); 33 | 34 | let animationId = null; 35 | 36 | const renderLoop = () => { 37 | // debugger; 38 | fps.render(); 39 | 40 | drawGrid(); 41 | drawCells(); 42 | // making time run faster 43 | // for (let i = 0; i < 9; i++) { 44 | universe.tick(); 45 | // } 46 | animationId = requestAnimationFrame(renderLoop); 47 | }; 48 | 49 | const drawGrid = () => { 50 | ctx.beginPath(); 51 | ctx.strokeStyle = GRID_COLOR; 52 | 53 | // Vertical lines 54 | for (let i = 0; i <= width; i++) { 55 | ctx.moveTo(i * (CELL_SIZE + 1) + 1, 0); 56 | ctx.lineTo(i * (CELL_SIZE + 1) + 1, (CELL_SIZE + 1) * height + 1); 57 | } 58 | 59 | // Horizontal lines 60 | for (let j = 0; j <= height; j++) { 61 | ctx.moveTo(0, j * (CELL_SIZE + 1) + 1); 62 | ctx.lineTo((CELL_SIZE + 1) * width + 1, j * (CELL_SIZE + 1) + 1); 63 | } 64 | }; 65 | 66 | const getIndex = (row, column) => { 67 | return row * width + column; 68 | }; 69 | 70 | const drawCells = () => { 71 | const cellsPtr = universe.cells(); 72 | const cells = new Uint8Array(memory.buffer, cellsPtr, width * height); 73 | ctx.beginPath(); 74 | // for (let row = 0; row < height; row++) { 75 | // for (let col = 0; col < width; col++) { 76 | // const idx = getIndex(row, col); 77 | // fillStyle property is set once for every cell in the universe, on every animation frame 78 | // 40% of our time is spent in this setter! 79 | // ctx.fillStyle = cells[idx] === Cell.Dead ? DEAD_COLOR : ALIVE_COLOR; 80 | // ctx.fillRect( 81 | // col * (CELL_SIZE + 1) + 1, 82 | // row * (CELL_SIZE + 1) + 1, 83 | // CELL_SIZE, 84 | // CELL_SIZE 85 | // ); 86 | 87 | // } 88 | // } 89 | 90 | // Alive cells 91 | ctx.fillStyle = ALIVE_COLOR; 92 | for (let row = 0; row < height; row++) { 93 | for (let col = 0; col < width; col++) { 94 | const idx = getIndex(row, col); 95 | if (cells[idx] !== Cell.Alive) { 96 | continue; 97 | } 98 | 99 | ctx.fillRect( 100 | col * (CELL_SIZE + 1) + 1, 101 | row * (CELL_SIZE + 1) + 1, 102 | CELL_SIZE, 103 | CELL_SIZE 104 | ); 105 | } 106 | } 107 | 108 | // Dead cells 109 | ctx.fillStyle = DEAD_COLOR; 110 | for (let row = 0; row < height; row++) { 111 | for (let col = 0; col < width; col++) { 112 | const idx = getIndex(row, col); 113 | if (cells[idx] !== Cell.Dead) { 114 | continue; 115 | } 116 | 117 | ctx.fillRect( 118 | col * (CELL_SIZE + 1) + 1, 119 | row * (CELL_SIZE + 1) + 1, 120 | CELL_SIZE, 121 | CELL_SIZE 122 | ); 123 | } 124 | } 125 | 126 | ctx.stroke(); 127 | }; 128 | 129 | // Pausing and Resuming the Game 130 | const isPaused = () => animationId === null; 131 | 132 | const playPauseButton = document.getElementById("play-pause"); 133 | 134 | const play = () => { 135 | playPauseButton.textContent = "⏸"; 136 | renderLoop(); 137 | }; 138 | 139 | const pause = () => { 140 | playPauseButton.textContent = "▶"; 141 | cancelAnimationFrame(animationId); 142 | animationId = null; 143 | }; 144 | 145 | playPauseButton.addEventListener("click", (event) => 146 | isPaused() ? play() : pause() 147 | ); 148 | 149 | // Toggling a Cell's State on "click" Events 150 | canvas.addEventListener("click", (event) => { 151 | const boundingRect = canvas.getBoundingClientRect(); 152 | 153 | const scaleX = canvas.width / boundingRect.width; 154 | const scaleY = canvas.height / boundingRect.height; 155 | 156 | const canvasLeft = (event.clientX - boundingRect.left) * scaleX; 157 | const canvasTop = (event.clientY - boundingRect.top) * scaleY; 158 | 159 | const row = Math.min(Math.floor(canvasTop / (CELL_SIZE + 1), height - 1)); 160 | const col = Math.min(Math.floor(canvasLeft / (CELL_SIZE + 1), width - 1)); 161 | 162 | universe.toggle_cell(row, col); 163 | 164 | drawGrid(); 165 | drawCells(); 166 | }); 167 | 168 | // Creating a Frames Per Second Timer 169 | const fps = new (class { 170 | constructor() { 171 | this.fps = document.getElementById("fps"); 172 | this.frames = []; 173 | this.lastFrameTimeStamp = performance.now(); 174 | } 175 | 176 | render() { 177 | // Convert the delta time since the last frame render into a measure 178 | // of frames per second. 179 | const now = performance.now(); 180 | const delta = now - this.lastFrameTimeStamp; 181 | this.lastFrameTimeStamp = now; 182 | const fps = (1 / delta) * 1000; 183 | 184 | // Save only the latest 100 timings. 185 | this.frames.push(fps); 186 | if (this.frames.length > 100) { 187 | this.frames.shift(); 188 | } 189 | 190 | // Find the max, min, and mean of our 100 latest timings. 191 | let min = Infinity; 192 | let max = -Infinity; 193 | let sum = 0; 194 | for (let i = 0; i < this.frames.length; i++) { 195 | sum += this.frames[i]; 196 | min = Math.min(this.frames[i], min); 197 | max = Math.max(this.frames[i], max); 198 | } 199 | let mean = sum / this.frames.length; 200 | 201 | // Render the statistics. 202 | // Optimize until you get 60 frames per second 203 | this.fps.textContent = ` 204 | Frames per second: 205 | Latest = ${Math.round(fps)} 206 | Avg of last 100 = ${Math.round(mean)} 207 | Min of last 100 = ${Math.round(min)} 208 | Max of last 100 = ${Math.round(max)} 209 | `; 210 | } 211 | })(); 212 | 213 | // Init 214 | drawGrid(); 215 | drawCells(); 216 | // requestAnimationFrame(renderLoop); 217 | play(); 218 | -------------------------------------------------------------------------------- /05-game-of-life/www/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-wasm-app", 3 | "version": "0.1.0", 4 | "description": "create an app to consume rust-generated wasm packages", 5 | "main": "index.js", 6 | "bin": { 7 | "create-wasm-app": ".bin/create-wasm-app.js" 8 | }, 9 | "scripts": { 10 | "build": "webpack --config webpack.config.js", 11 | "start": "webpack-dev-server" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/rustwasm/create-wasm-app.git" 16 | }, 17 | "keywords": [ 18 | "webassembly", 19 | "wasm", 20 | "rust", 21 | "webpack" 22 | ], 23 | "author": "Ashley Williams ", 24 | "license": "(MIT OR Apache-2.0)", 25 | "bugs": { 26 | "url": "https://github.com/rustwasm/create-wasm-app/issues" 27 | }, 28 | "homepage": "https://github.com/rustwasm/create-wasm-app#readme", 29 | "dependencies": { 30 | "game-of-life": "file:../pkg" 31 | }, 32 | "devDependencies": { 33 | "hello-wasm-pack": "^0.1.0", 34 | "webpack": "^4.29.3", 35 | "webpack-cli": "^3.1.0", 36 | "webpack-dev-server": "^3.1.5", 37 | "copy-webpack-plugin": "^5.0.0" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /05-game-of-life/www/webpack.config.js: -------------------------------------------------------------------------------- 1 | const CopyWebpackPlugin = require("copy-webpack-plugin"); 2 | const path = require('path'); 3 | 4 | module.exports = { 5 | entry: "./bootstrap.js", 6 | output: { 7 | path: path.resolve(__dirname, "dist"), 8 | filename: "bootstrap.js", 9 | }, 10 | mode: "development", 11 | plugins: [ 12 | new CopyWebpackPlugin(['index.html']) 13 | ], 14 | }; 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust Projects 2 | 3 | 5 small projects to understand Rust core concepts. 4 | 5 | | # | Project | Description | 6 | | --- | ------------------------------ | ---------------------------------------------------------------------------------- | 7 | | 01 | [**Hello Cargo**](#hellocargo) | A quick intro to Rust syntax. | 8 | | 02 | [**Guessing Game**](#guessing) | A hands-on introduction to the Rust language. | 9 | | 03 | [**Minigrep**](#minigrep) | A simple version of the classic command line tool grep. | 10 | | 04 | [**Hello**](#hello) | A multithreaded web server that says “hello”. | 11 | | 05 | [**Game of Life**](#life) | A zero-player game to learn how to use Rust, WebAssembly, and JavaScript together. | 12 | 13 | ## Get Inspired 14 | 15 | Check out our [**collection of articles**](https://www.onbusinessplan.com/) for those beginning their Rust journey. Find tips, tricks, and motivational content to keep you engaged and motivated throughout your learning process. 16 | 17 | ## 1) Hello Cargo 18 | 19 | A quick intro to Rust syntax. 20 | 21 | [See 01-hello-cargo folder](01-hello-cargo) 22 | 23 | ### Features 24 | 25 | - installing Rustup, [Rust for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust) and [Even Better TOML](https://marketplace.visualstudio.com/items?itemName=tamasfe.even-better-toml). 26 | - setting up a project with Cargo. 27 | - printing and formatting strings. 28 | - handling variables. 29 | - discovering primitive types. 30 | - playing with strings, tuples, arrays and vectors. 31 | - rendering conditionals. 32 | - using loops, functions and pointer references. 33 | - creating custom data types with structs. 34 | - defining enums. 35 | - grabbing args from CLI. 36 | 37 | ## 2) Guessing Game 38 | 39 | A hands-on introduction to the Rust language. 40 | 41 | [See 02-guessing-game folder](02-guessing-game) 42 | 43 | ### Features 44 | 45 | - handling user input with the io library. 46 | - adding rand from [crates.io](https://crates.io/) as a dependency. 47 | - generating a secret number. 48 | - comparing the guess to the secret number. 49 | - allowing multiple guesses with looping. 50 | - quitting after a correct guess. 51 | - handling invalid input. 52 | 53 | ## 3) Minigrep 54 | 55 | A simple version of the classic command line tool grep. 56 | 57 | [See 03-minigrep folder](03-minigrep) 58 | 59 | ### Features 60 | 61 | - accepting command line arguments. 62 | - reading a file. 63 | - refactoring to improve modularity and error handling. 64 | - developing the library’s functionality with Test-Driven Development (TDD). 65 | - working with environment variables. 66 | - writing error messages to standard error instead of standard output. 67 | - using closures and iterators. 68 | 69 | ## 4) Hello 70 | 71 | A multithreaded web server that says “hello”. 72 | 73 | [See 04-hello folder](04-hello) 74 | 75 | ### Features 76 | 77 | - listening to the TCP connection and reading the request. 78 | - writing a response and returning HTML. 79 | - validating the request and selectively responding. 80 | - simulating a slow request. 81 | - improving throughput with a thread pool. 82 | - handling graceful shutdown. 83 | 84 | ## 5) Game of Life 85 | 86 | A zero-player game to learn how to use Rust, WebAssembly, and JavaScript together. 87 | 88 | [See 05-game-of-life folder](05-game-of-life) 89 | 90 |

91 | 92 | 93 | 94 |

95 | 96 | ### Features 97 | 98 | - cloning the project template and understanding the folder structure. 99 | - building the project with wasm-pack. 100 | - putting it into a web page with wasm-app. 101 | - serving locally with webpack. 102 | - implementing the Game of Life with Rust. 103 | - rendering to canvas directly from memory with JavaScript. 104 | - testing the tick function and debugging. 105 | - pausing and resuming the game. 106 | - toggling a cell's state on click. 107 | - optimizing performances with time profiling and benchmarking. 108 | - shrinking .wasm size with wasm-opt and gzip. 109 | 110 | ## Acknowledgments 111 | 112 | These projects are adapted from various sources: 113 | 114 | - [Rust Crash Course](https://www.youtube.com/watch?v=zF34dRivLOw) by Brad Traversy (2019) 115 | - [The Rust Programming Language](https://doc.rust-lang.org/book/) by Steve Klabnik and Carol Nichols (2021) 116 | - [The Rust and WebAssembly Book](https://rustwasm.github.io/docs/book/) by The Rust and WebAssembly Working Group (2021) 117 | 118 | ## Show Your Support 119 | 120 | If you find these projects helpful or interesting, please consider starring the repository. It's a simple gesture that helps to boost the visibility of the project and show appreciation for the effort put into creating it. Additionally, if you'd like to support my work further, you can [**become a sponsor**](https://github.com/sponsors/solygambas). Your support is greatly appreciated. Thank you! 121 | -------------------------------------------------------------------------------- /playground/01-rust-sandbox/commands.md: -------------------------------------------------------------------------------- 1 | # Useful Commands 2 | 3 | - rustup --version 4 | - rustup update 5 | - rustc --version 6 | - cargo --version 7 | - rustc hello.rs 8 | - ./hello 9 | -------------------------------------------------------------------------------- /playground/01-rust-sandbox/hello.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello World!") // ! stands for macro 3 | } -------------------------------------------------------------------------------- /playground/02-programming-concepts/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "programming_concepts" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /playground/02-programming-concepts/src/control.rs: -------------------------------------------------------------------------------- 1 | pub fn run() { 2 | // let number = 3; 3 | // if number < 5 { 4 | // println!("condition was true"); 5 | // } else { 6 | // println!("condition was false"); 7 | // } 8 | // if number != 0 { 9 | // println!("number was something other than zero"); 10 | // } 11 | 12 | // let number = 6; 13 | // if number % 4 == 0 { 14 | // println!("number is divisible by 4"); 15 | // } else if number % 3 == 0 { 16 | // println!("number is divisible by 3"); 17 | // } else if number % 2 == 0 { 18 | // println!("number is divisible by 2"); 19 | // } else { 20 | // println!("number is divisible by 4, 3, or 2"); 21 | // } 22 | // number is divisible by 3 23 | 24 | // let condition = true; 25 | // let number = if condition { 5 } else { 6 }; 26 | // println!("The value of number is: {}", number); // 5 27 | 28 | // loop { 29 | // println!("again"); 30 | // break; 31 | // } 32 | 33 | // loops with labels 34 | // let mut count = 0; 35 | // 'counting_up: loop { 36 | // println!("count = {}", count); 37 | // let mut remaining = 10; 38 | 39 | // loop { 40 | // println!("remaining = {}", remaining); 41 | // if remaining == 9 { 42 | // break; 43 | // } 44 | // if count == 2 { 45 | // break 'counting_up; 46 | // } 47 | // remaining -= 1; 48 | // } 49 | // count += 1; 50 | // } 51 | // println!("End count = {}", count); 52 | 53 | // return value after break 54 | // let mut counter = 0; 55 | // let result = loop { 56 | // counter += 1; 57 | // if counter == 10 { 58 | // break counter * 2; 59 | // } 60 | // }; 61 | // println!("The result is {}", result); // 20 62 | 63 | // while loop 64 | // let mut number = 3; 65 | // while number != 0 { 66 | // println!("{}!", number); 67 | // number -= 1; 68 | // } 69 | // println!("LIFTOFF!!!") 70 | // better way: 71 | // for number in (1..4).rev() { 72 | // println!("{}!", number); 73 | // } 74 | // println!("LIFTOFF!!!") 75 | 76 | // for loop 77 | // let a = [10, 20, 30, 40, 50]; 78 | // let mut index = 0; 79 | // error prone and slow: 80 | // while index < 5 { 81 | // println!("the value is: {}", a[index]); 82 | // index += 1; 83 | // } 84 | // safety and conciseness: 85 | // for element in a { 86 | // println!("the value is: {}", element); 87 | // } 88 | } 89 | -------------------------------------------------------------------------------- /playground/02-programming-concepts/src/functions.rs: -------------------------------------------------------------------------------- 1 | pub fn run() { 2 | println!("Hello, world!"); 3 | // another_function(5); 4 | // print_labeled_measurement(5, 'h'); 5 | 6 | // statements and expressions 7 | // let y = { 8 | // let x = 3; // statement 9 | // x + 1 // expression 10 | // }; 11 | // println!("The value of y is: {}", y); 12 | 13 | // return value 14 | let x = five(); 15 | println!("The value of x is: {}", x); 16 | } 17 | 18 | // fn another_function(x: i32) { 19 | // println!("The value of x is: {}", x); 20 | // } 21 | 22 | // fn print_labeled_measurement(value: i32, unit_label: char) { 23 | // println!("The measurement is: {}{}", value, unit_label); 24 | // } 25 | 26 | // return value 27 | fn five() -> i32 { 28 | 5 29 | } -------------------------------------------------------------------------------- /playground/02-programming-concepts/src/main.rs: -------------------------------------------------------------------------------- 1 | // https://www.raulmelo.dev/til/how-to-format-rust-code-on-save-in-vscode 2 | 3 | // mod vars; 4 | // mod types; 5 | // mod functions; 6 | mod control; 7 | 8 | fn main() { 9 | // vars::run(); 10 | // types::run(); 11 | // functions::run(); 12 | control::run(); 13 | } 14 | -------------------------------------------------------------------------------- /playground/02-programming-concepts/src/types.rs: -------------------------------------------------------------------------------- 1 | pub fn run() { 2 | // floating-point types 3 | let x = 2.0; // f64 4 | let y: f32 = 3.0; // f32 5 | 6 | // boolean 7 | let t = true; 8 | let f: bool = false; 9 | 10 | // character type 11 | let z = 'Z'; 12 | let heart_eyed_cat = '😻'; 13 | println!("{}", heart_eyed_cat); 14 | 15 | // tuples 16 | let tup = (500, 6.4, "John"); 17 | let (x, y, z) = tup; // destructuring 18 | println!("The value of y is {}", y); 19 | let x: (i32, f64, u8) = (500, 6.4, 1); 20 | let five_hundred = x.0; 21 | let six_point_four = x.1; 22 | let one = x.2; 23 | 24 | // arrays 25 | let a: [i32; 5] = [1, 2, 3, 4, 5]; 26 | let first = a[0]; 27 | let second = a[1]; 28 | // if you try a[5], runtime error - index out of bounds: the len is 4 but the index is 5 29 | } -------------------------------------------------------------------------------- /playground/02-programming-concepts/src/vars.rs: -------------------------------------------------------------------------------- 1 | pub fn run() { 2 | // mutability 3 | let mut x = 5; 4 | println!("The value of x is: {}", x); 5 | x = 6; 6 | println!("The value of x is: {}", x); 7 | // constants 8 | // const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3; 9 | // shadowing 10 | let x = 5; 11 | let x = x + 1; 12 | { 13 | let x = x * 2; 14 | println!("The value of x in the inner scope is: {}", x); // 12 15 | } 16 | println!("The value of x is: {}", x); // 6 17 | // change the type of the value but reuse the same name 18 | let spaces = " "; // string 19 | let spaces = spaces.len(); // number 20 | // won't work with let mut spaces and spaces (not allowed to mutate a variable’s type) 21 | } -------------------------------------------------------------------------------- /playground/03-ownership/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ownership" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /playground/03-ownership/src/basics.rs: -------------------------------------------------------------------------------- 1 | // pub fn run() { 2 | // // String: for a value we don't know when we write our code 3 | // let mut s = String::from("hello"); // pointer, length and capacity are in the stack, the heap holds the content "hello" 4 | // s.push_str(", world!"); 5 | // println!("{}", s); 6 | // } // drop is called by Rust: s is cleaned from memory 7 | 8 | // pub fn run() { 9 | // // stack data 10 | // let x = 5; 11 | // let y = x; // shallow copy 12 | // println!("x = {}, y = {}", x, y); 13 | // // heap data 14 | // let s1 = String::from("hello"); 15 | // let s2 = s1.clone(); // without clone, s1 is dropped to avoid "double free error" 16 | // println!("s1 = {}, s2 = {}", s1, s2); 17 | // } 18 | 19 | pub fn run() { 20 | let s = String::from("hello"); 21 | takes_ownership(s); 22 | // s is no longer valid here (borrow of moved value) 23 | let x = 5; 24 | makes_copy(x); // shallow copy 25 | println!("{}", x); // x is still valid 26 | } 27 | 28 | fn takes_ownership(some_string: String) { 29 | println!("{}", some_string); 30 | } 31 | 32 | fn makes_copy(some_integer: i32) { 33 | println!("{}", some_integer); 34 | } 35 | -------------------------------------------------------------------------------- /playground/03-ownership/src/main.rs: -------------------------------------------------------------------------------- 1 | // mod basics; 2 | // mod references; 3 | mod slices; 4 | 5 | fn main() { 6 | // basics::run(); 7 | // references::run(); 8 | slices::run(); 9 | } 10 | -------------------------------------------------------------------------------- /playground/03-ownership/src/references.rs: -------------------------------------------------------------------------------- 1 | pub fn run() { 2 | let s1 = String::from("hello"); 3 | // borrowing 4 | let len = calculate_length(&s1); // & refers to some value without taking ownership of it (no need to return) 5 | println!("The length of '{}' is {}", s1, len); 6 | 7 | // mutable reference 8 | let mut s2 = String::from("hello"); 9 | change(&mut s2); 10 | println!("{}", s2); // hello, world 11 | } 12 | 13 | fn calculate_length(s: &String) -> usize { 14 | s.len() 15 | } // s goes out of scope, nothing happens to s1 because it does not have ownership on it 16 | 17 | fn change(some_string: &mut String) { 18 | some_string.push_str(", world"); 19 | } 20 | -------------------------------------------------------------------------------- /playground/03-ownership/src/slices.rs: -------------------------------------------------------------------------------- 1 | pub fn run() { 2 | // let mut s = String::from("hello world"); 3 | // let word = first_word(&s); 4 | // s.clear(); // index becomes invalid 5 | 6 | // string slice 7 | // let s = String::from("hello world"); 8 | // let hello = &s[0..5]; // &s[..5] 9 | // let world = &s[6..11]; // &s[6..] 10 | // let slice = &s[0..s.len()]; // &s[..] 11 | 12 | let s2 = String::from("hello world"); 13 | let word2 = first_word(&s2); // we can pass references or slices 14 | println!("The first word is: {}", word2); // hello 15 | 16 | // array slice 17 | let a = [1, 2, 3, 4, 5]; 18 | let slice = &a[1..3]; 19 | assert_eq!(slice, &[2, 3]); 20 | } 21 | 22 | // fn first_word(s: &String) -> usize { 23 | // let bytes = s.as_bytes(); 24 | // for (i, &item) in bytes.iter().enumerate() { 25 | // if item == b' ' { 26 | // return i; 27 | // } 28 | // } 29 | // s.len() 30 | // } 31 | 32 | fn first_word(s: &str) -> &str { 33 | let bytes = s.as_bytes(); 34 | for (i, &item) in bytes.iter().enumerate() { 35 | if item == b' ' { 36 | return &s[0..i]; 37 | } 38 | } 39 | &s[..] 40 | } 41 | -------------------------------------------------------------------------------- /playground/04-structs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "structs" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /playground/04-structs/src/basics.rs: -------------------------------------------------------------------------------- 1 | pub fn run() { 2 | let mut user1 = User { 3 | email: String::from("someone@example.com"), 4 | username: String::from("someusername"), 5 | active: true, 6 | sign_in_count: 1, 7 | }; 8 | user1.email = String::from("anotheremail@example.com"); 9 | println!("{}", user1.email); 10 | 11 | // struct update syntax 12 | let user2 = User { 13 | email: String::from("anotheremail@example.com"), 14 | ..user1 15 | }; 16 | println!("{}", user2.username); 17 | 18 | // tuple struct 19 | struct Color(i32, i32, i32); 20 | let black = Color(0, 0, 0); 21 | 22 | // unit-like structs 23 | struct AlwaysEqual; 24 | let subject = AlwaysEqual; 25 | } 26 | 27 | struct User { 28 | active: bool, 29 | username: String, 30 | email: String, 31 | sign_in_count: u64, 32 | } 33 | 34 | fn build_user(email: String, username: String) -> User { 35 | User { 36 | email, // field init shorthand 37 | username, 38 | active: true, 39 | sign_in_count: 1, 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /playground/04-structs/src/main.rs: -------------------------------------------------------------------------------- 1 | // mod basics; 2 | // mod rectangles; 3 | mod method; 4 | 5 | fn main() { 6 | // basics::run(); 7 | // rectangles::run(); 8 | method::run(); 9 | } 10 | -------------------------------------------------------------------------------- /playground/04-structs/src/method.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | 3 | struct Rectangle { 4 | width: u32, 5 | height: u32, 6 | } 7 | 8 | impl Rectangle { 9 | fn area(&self) -> u32 { 10 | self.width * self.height 11 | } 12 | 13 | // can be useful as a getter 14 | fn width(&self) -> bool { 15 | self.width > 0 16 | } 17 | 18 | // more parameters 19 | fn can_hold(&self, other: &Rectangle) -> bool { 20 | self.width > other.width && self.height > other.height 21 | } 22 | 23 | // associated function 24 | fn square(size: u32) -> Rectangle { 25 | Rectangle { 26 | width: size, 27 | height: size, 28 | } 29 | } 30 | } 31 | 32 | pub fn run() { 33 | let rect1 = Rectangle { 34 | width: 30, 35 | height: 50, 36 | }; 37 | println!( 38 | "The area of the rectangle is {} square pixels", 39 | rect1.area() 40 | ); 41 | if rect1.width() { 42 | println!("The rectangle has a nonzero width; it is {}", rect1.width); 43 | } 44 | 45 | let rect2 = Rectangle { 46 | width: 10, 47 | height: 40, 48 | }; 49 | let rect3 = Rectangle { 50 | width: 60, 51 | height: 45, 52 | }; 53 | 54 | println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2)); // true 55 | println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3)); // false 56 | 57 | // call an associated function 58 | let sq = Rectangle::square(3); 59 | println!("The square is {:?}", sq); 60 | } 61 | -------------------------------------------------------------------------------- /playground/04-structs/src/rectangles.rs: -------------------------------------------------------------------------------- 1 | // pub fn run() { 2 | // let width1 = 30; 3 | // let height1 = 50; 4 | // println!( 5 | // "The area of the rectangle is {} square pixels", 6 | // area(width1, height1) 7 | // ); 8 | // } 9 | 10 | // fn area(width: u32, height: u32) -> u32 { 11 | // width * height 12 | // } 13 | 14 | // refactoring with tuples 15 | // pub fn run() { 16 | // let rect1 = (30, 50); 17 | // println!("The area of the rectangle is {} square pixels", area(rect1)); 18 | // } 19 | 20 | // fn area(dimensions: (u32, u32)) -> u32 { 21 | // dimensions.0 * dimensions.1 22 | // } 23 | 24 | // refactoring with structs 25 | 26 | #[derive(Debug)] // debug trait 27 | 28 | struct Rectangle { 29 | width: u32, 30 | height: u32, 31 | } 32 | 33 | pub fn run() { 34 | let rect1 = Rectangle { 35 | width: 30, 36 | height: 50, 37 | }; 38 | println!("rect1 is {:?}", rect1); 39 | println!("rect1 is {:#?}", rect1); // on several lines 40 | dbg!(&rect1); // figure out what your code is doing 41 | println!( 42 | "The area of the rectangle is {} square pixels", 43 | area(&rect1) 44 | ); 45 | } 46 | 47 | fn area(rectangle: &Rectangle) -> u32 { 48 | rectangle.width * rectangle.height 49 | } 50 | -------------------------------------------------------------------------------- /playground/05-enums/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "enums" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /playground/05-enums/src/definition.rs: -------------------------------------------------------------------------------- 1 | enum IpAddrKind { 2 | V4, 3 | V6, 4 | } 5 | 6 | enum IpAddr { 7 | V4(u8, u8, u8, u8), 8 | V6(String), 9 | } 10 | 11 | // with structs 12 | // struct Ipv4Addr; 13 | // struct Ipv6Addr; 14 | 15 | // enum IpAddr { 16 | // V4(Ipv4Addr), 17 | // V6(Ipv6Addr), 18 | // } 19 | 20 | // with various types 21 | enum Message { 22 | Quit, 23 | Move { x: i32, y: i32 }, 24 | Write(String), 25 | ChangeColor(i32, i32, i32), 26 | } 27 | 28 | // method 29 | impl Message { 30 | fn call(&self) {} 31 | } 32 | 33 | // option enum (handling null) 34 | enum Option { 35 | None, 36 | Some(T), 37 | } 38 | 39 | pub fn run() { 40 | // let four = IpAddrKind::V4; 41 | // let six = IpAddrKind::V6; 42 | // route(IpAddrKind::V4); 43 | 44 | let home = IpAddr::V4(127, 0, 0, 1); 45 | let loopback = IpAddr::V6(String::from("::1")); 46 | 47 | let m = Message::Write(String::from("hello")); 48 | m.call(); 49 | 50 | let some_number = Some(5); 51 | let some_string = Some("a string"); 52 | let absent_number: Option = Option::None; 53 | } 54 | 55 | fn route(ip_kind: IpAddrKind) {} 56 | -------------------------------------------------------------------------------- /playground/05-enums/src/if_let.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | 3 | enum UsState { 4 | Alabama, 5 | Alaska, 6 | } 7 | 8 | enum Coin { 9 | Penny, 10 | Nickel, 11 | Dime, 12 | Quarter(UsState), 13 | } 14 | 15 | pub fn run() { 16 | // if let 17 | let config_max = Some(3u8); 18 | if let Some(max) = config_max { 19 | println!("The maximum is configured to be {}", max); // 3 20 | } 21 | 22 | // if else 23 | let mut count = 0; 24 | let coin = Coin::Quarter(UsState::Alabama); 25 | if let Coin::Quarter(state) = coin { 26 | println!("State quarter from {:?}!", state); 27 | } else { 28 | count += 1; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /playground/05-enums/src/main.rs: -------------------------------------------------------------------------------- 1 | // mod definition; 2 | // mod match_operator; 3 | mod if_let; 4 | 5 | fn main() { 6 | // definition::run(); 7 | // match_operator::run(); 8 | if_let::run(); 9 | } 10 | -------------------------------------------------------------------------------- /playground/05-enums/src/match_operator.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | 3 | enum UsState { 4 | Alabama, 5 | Alaska, 6 | } 7 | 8 | enum Coin { 9 | Penny, 10 | Nickel, 11 | Dime, 12 | Quarter(UsState), 13 | } 14 | 15 | pub fn run() { 16 | // value_in_cents(Coin::Penny); 17 | // value_in_cents(Coin::Quarter(UsState::Alaska)); 18 | 19 | let five = Some(5); 20 | let six = plus_one(five); // 6 21 | let none = plus_one(None); // None 22 | 23 | dice_roll(9); 24 | } 25 | 26 | fn value_in_cents(coin: Coin) -> u8 { 27 | match coin { 28 | Coin::Penny => { 29 | println!("Lucky penny!"); 30 | 1 31 | } 32 | Coin::Nickel => 5, 33 | Coin::Dime => 10, 34 | Coin::Quarter(state) => { 35 | println!("State quarter from {:?}", state); 36 | 1 37 | } 38 | } 39 | } 40 | 41 | // matching with Option 42 | fn plus_one(x: Option) -> Option { 43 | match x { 44 | None => None, 45 | Some(i) => Some(i + 1), 46 | } 47 | } 48 | 49 | // catch-all patterns 50 | fn dice_roll(number: u8) { 51 | match number { 52 | 3 => add_fancy_hat(), 53 | 7 => remove_fancy_hat(), 54 | // other => move_player(other), // catch-all pattern 55 | // _ => reroll(), // placeholder pattern 56 | _ => (), // empty tuple, do nothing 57 | } 58 | } 59 | 60 | fn add_fancy_hat() {} 61 | fn remove_fancy_hat() {} 62 | fn reroll() {} 63 | fn move_player(num_spaces: u8) { 64 | println!("{}", num_spaces); 65 | } 66 | -------------------------------------------------------------------------------- /playground/06-managing-projects/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "managing_projects" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | rand = "0.8.3" 10 | -------------------------------------------------------------------------------- /playground/06-managing-projects/src/front_of_house.rs: -------------------------------------------------------------------------------- 1 | pub mod hosting; 2 | -------------------------------------------------------------------------------- /playground/06-managing-projects/src/front_of_house/hosting.rs: -------------------------------------------------------------------------------- 1 | pub fn add_to_waitlist() {} 2 | -------------------------------------------------------------------------------- /playground/06-managing-projects/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod front_of_house; 2 | 3 | // pub fn eat_at_restaurant() { 4 | // // Absolute path 5 | // crate::front_of_house::hosting::add_to_waitlist(); 6 | // // Relative path 7 | // front_of_house::hosting::add_to_waitlist(); 8 | // } 9 | 10 | fn serve_order() {} 11 | 12 | mod back_of_house { 13 | fn fix_incorrect_order() { 14 | cook_order(); 15 | super::serve_order(); // go to crate (root), the parent module of back_of_house 16 | } 17 | 18 | fn cook_order() {} 19 | 20 | pub struct Breakfast { 21 | pub toast: String, 22 | seasonal_fruit: String, 23 | } 24 | 25 | impl Breakfast { 26 | pub fn summer(toast: &str) -> Breakfast { 27 | Breakfast { 28 | toast: String::from(toast), 29 | seasonal_fruit: String::from("peaches"), 30 | } 31 | } 32 | } 33 | 34 | pub enum Appetizer { 35 | Soup, 36 | Salad, 37 | } 38 | } 39 | 40 | // pub fn eat_at_restaurant() { 41 | // // Order a breakfast 42 | // let mut meal = back_of_house::Breakfast::summer("Rye"); 43 | // // Change your mind 44 | // meal.toast = String::from("Wheat"); 45 | // println!("I'd like {} toast please", meal.toast); 46 | // // enum 47 | // let order1 = back_of_house::Appetizer::Soup; 48 | // let order1 = back_of_house::Appetizer::Salad; 49 | 50 | // } 51 | 52 | // use keyword 53 | use crate::front_of_house::hosting; 54 | // use self::front_of_house::hosting; 55 | 56 | // full path for structs, enums, etc. 57 | use std::collections::HashMap; 58 | 59 | // as 60 | use std::io::Result as IoResult; 61 | 62 | // re-exporting 63 | // pub use crate::back_of_house::Breakfast; 64 | 65 | // external package 66 | use rand::Rng; 67 | 68 | // nested paths 69 | // use std::{cmp::Ordering, io}; 70 | use std::io::{self, Write}; 71 | 72 | // glob operator 73 | use std::collections::*; 74 | 75 | pub fn eat_at_restaurant() { 76 | hosting::add_to_waitlist(); 77 | let mut map = HashMap::new(); 78 | map.insert(1, 2); 79 | } 80 | -------------------------------------------------------------------------------- /playground/06-managing-projects/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /playground/07-collections/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "collections" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /playground/07-collections/src/hash_maps.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | pub fn run() { 4 | // creating 5 | let mut scores = HashMap::new(); 6 | scores.insert(String::from("Blue"), 10); 7 | scores.insert(String::from("Yellow"), 50); 8 | 9 | let teams = vec![String::from("Blue"), String::from("Yellow")]; 10 | let initial_scores = vec![10, 50]; 11 | let mut scores2: HashMap<_, _> = teams.into_iter().zip(initial_scores.into_iter()).collect(); 12 | // zip creates an iterator of tuples > collect turns that into a hash map 13 | 14 | // ownership 15 | let field_name = String::from("Favorite color"); 16 | let field_value = String::from("Blue"); 17 | let mut map = HashMap::new(); 18 | map.insert(field_name, field_value); 19 | // field_name & field_value are no longer valid at this point 20 | 21 | // accessing values 22 | let team_name = String::from("Blue"); 23 | let score = scores.get(&team_name); // Some(&10) 24 | 25 | // iterating 26 | for (key, value) in &scores { 27 | println!("{}: {}", key, value); // Blue: 10, Yellow: 50 28 | } 29 | 30 | // updating 31 | // overwriting 32 | scores.insert(String::from("Green"), 30); 33 | scores.insert(String::from("Green"), 25); 34 | 35 | // inserting if the key has no value 36 | scores.entry(String::from("Red")).or_insert(50); 37 | 38 | // updating based on the old value 39 | let text = "hello world wonderful world"; 40 | let mut map = HashMap::new(); 41 | for word in text.split_whitespace() { 42 | let count = map.entry(word).or_insert(0); 43 | *count += 1; 44 | } 45 | println!("{:?}", map); // {"wonderful": 1, "world": 2, "hello": 1} 46 | } 47 | -------------------------------------------------------------------------------- /playground/07-collections/src/main.rs: -------------------------------------------------------------------------------- 1 | // mod vectors; 2 | // mod strings; 3 | mod hash_maps; 4 | 5 | fn main() { 6 | // vectors::run(); 7 | // strings::run(); 8 | hash_maps::run(); 9 | } 10 | -------------------------------------------------------------------------------- /playground/07-collections/src/strings.rs: -------------------------------------------------------------------------------- 1 | pub fn run() { 2 | // creating 3 | let mut s = String::new(); 4 | let data = "initial contents"; 5 | let s2 = data.to_string(); 6 | let s3 = "initial contents".to_string(); 7 | let s4 = String::from("initial contents"); 8 | 9 | // updating 10 | let mut s5 = String::from("foo"); 11 | s5.push_str("bar"); // foobar 12 | let mut s6 = String::from("lo"); 13 | s.push('l'); // lol 14 | 15 | // concatenating 16 | let s7 = String::from("Hello, "); 17 | let s8 = String::from("world!"); 18 | let s9 = s7 + &s8; // Hello, world! // note s1 has been moved here and can no longer be used: fn add(self, s: &str) 19 | 20 | let s10 = String::from("tic"); 21 | let s11 = String::from("tac"); 22 | let s12 = String::from("toe"); 23 | let s13 = format!("{}-{}-{}", s10, s11, s12); // tic-tac-toe 24 | 25 | // slicing 26 | let hello = "Здравствуйте"; 27 | let s14 = &hello[0..4]; // 4 bytes, each character is 2 bytes: Зд 28 | 29 | // iterating 30 | // Unicode scalar values 31 | for c in "नमस्ते".chars() { 32 | println!("{}", c); 33 | } 34 | // raw byte 35 | for b in "नमस्ते".bytes() { 36 | println!("{}", b); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /playground/07-collections/src/vectors.rs: -------------------------------------------------------------------------------- 1 | // https://doc.rust-lang.org/std/vec/struct.Vec.html 2 | 3 | pub fn run() { 4 | // creating 5 | // let v: Vec = Vec::new(); 6 | // let v = vec![1, 2, 3]; 7 | 8 | // updating 9 | // let mut v = Vec::new(); 10 | // v.push(5); 11 | // v.push(7); 12 | 13 | // reading 14 | // let v = vec![1, 2, 3, 4, 5]; 15 | // let third: &i32 = &v[2]; 16 | // println!("The third element is {}", third); 17 | // match v.get(2) { 18 | // Some(third) => println!("The third element is {}", third), 19 | // None => println!("There is no third element."), 20 | // } 21 | 22 | // deleting 23 | // let mut v = vec![1, 2, 3, 4, 5]; 24 | // v.pop(); 25 | 26 | // iterating 27 | // let v = vec![100, 32, 57]; 28 | // for integer in &v { 29 | // println! {"{}", integer}; // 100 32 57 30 | // } 31 | // let mut v = vec![100, 32, 57]; 32 | // for integer in &mut v { 33 | // *integer += 50; 34 | // println! {"{}", integer}; // 150 82 107 35 | // } 36 | 37 | // using an enum to store multiple types 38 | enum SpreadsheetCell { 39 | Int(i32), 40 | Float(f64), 41 | Text(String), 42 | } 43 | 44 | let row = vec![ 45 | SpreadsheetCell::Int(3), 46 | SpreadsheetCell::Float(10.12), 47 | SpreadsheetCell::Text(String::from("blue")), 48 | ]; 49 | } 50 | -------------------------------------------------------------------------------- /playground/08-error-handling/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "error_handling" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | 10 | # [profile.release] 11 | # panic = 'abort' # ends the program without cleaning up 12 | -------------------------------------------------------------------------------- /playground/08-error-handling/hello.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solygambas/rust-projects/7b34af69709e62162549c74f7ac015e623a7649f/playground/08-error-handling/hello.txt -------------------------------------------------------------------------------- /playground/08-error-handling/src/main.rs: -------------------------------------------------------------------------------- 1 | // mod unrecoverable_errors; 2 | mod recoverable_errors; 3 | 4 | fn main() { 5 | // unrecoverable_errors::run(); 6 | recoverable_errors::run(); 7 | } 8 | -------------------------------------------------------------------------------- /playground/08-error-handling/src/recoverable_errors.rs: -------------------------------------------------------------------------------- 1 | // use std::fs::File; 2 | use std::fs; 3 | // use std::io::ErrorKind; 4 | use std::error::Error; 5 | use std::fs::File; 6 | use std::io::{self, Read}; 7 | 8 | // pub fn run() { 9 | // let f = File::open("hello.txt"); 10 | 11 | // with match 12 | // let f = match f { 13 | // Ok(file) => file, 14 | // // Err(error) => panic!("Problem opening the file: {:?}", error), 15 | // Err(error) => match error.kind() { 16 | // ErrorKind::NotFound => match File::create("hello.txt") { 17 | // Ok(fc) => fc, 18 | // Err(e) => panic!("Problem creating the file: {:?}", e), 19 | // }, 20 | // other_error => { 21 | // panic!("Problem opening the file: {:?}", other_error) 22 | // } 23 | // }, 24 | // }; 25 | 26 | // with closures 27 | // let f = File::open("hello.txt").unwrap_or_else(|error| { 28 | // if error.kind() == ErrorKind::NotFound { 29 | // File::create("hello.txt").unwrap_or_else(|error| { 30 | // panic!("Problem creating the file: {:?}", error); 31 | // }) 32 | // } else { 33 | // panic!("Problem opening the file: {:?}", error); 34 | // } 35 | // }); 36 | 37 | // unwrap 38 | // let f = File::open("hello.txt").unwrap(); // generic panic 39 | 40 | // expect 41 | // let f = File::open("hello.txt").expect("Failed to open hello.txt"); // custom message 42 | // } 43 | 44 | // propagating errors 45 | // pub fn run() -> Result { 46 | // let f = File::open("hello.txt"); 47 | // let mut f = match f { 48 | // Ok(file) => file, 49 | // Err(e) => return Err(e), 50 | // }; 51 | // let mut s = String::new(); 52 | // match f.read_to_string(&mut s) { 53 | // Ok(_) => Ok(s), 54 | // Err(e) => Err(e), 55 | // } 56 | // } 57 | 58 | // with the ? operator 59 | // pub fn run() -> Result { 60 | // let mut f = File::open("hello.txt")?; 61 | // let mut s = String::new(); 62 | // f.read_to_string(&mut s)?; 63 | // Ok(s) 64 | // } 65 | 66 | // chaining methods 67 | // pub fn run() -> Result { 68 | // let mut s = String::new(); 69 | // File::open("hello.txt")?.read_to_string(&mut s)?; 70 | // Ok(s) 71 | // } 72 | 73 | // with fs module 74 | pub fn run() -> Result { 75 | fs::read_to_string("hello.txt") 76 | } 77 | 78 | // with option 79 | fn last_char_of_first_line(text: &str) -> Option { 80 | text.lines().next()?.chars().last() 81 | } 82 | 83 | // with a trait object 84 | fn any_kind_of_error() -> Result<(), Box> { 85 | let f = File::open("hello.txt")?; // with ?, an Err value can be returned early 86 | Ok(()) 87 | } 88 | -------------------------------------------------------------------------------- /playground/08-error-handling/src/unrecoverable_errors.rs: -------------------------------------------------------------------------------- 1 | pub fn run() { 2 | // panic!("crash and burn"); 3 | // thread 'main' panicked at 'crash and burn', src\unrecoverable_errors.rs:2:5 4 | // 2nd line, 5th character 5 | 6 | let v = vec![1, 2, 3]; 7 | v[99]; // thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 99' 8 | } 9 | 10 | // RUST_BACKTRACE=1 cargo run 11 | //6: error_handling::unrecoverable_errors::run at .\src\unrecoverable_errors.rs:7 12 | -------------------------------------------------------------------------------- /playground/09-generic-types/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "generic_types" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /playground/09-generic-types/src/duplicate.rs: -------------------------------------------------------------------------------- 1 | fn largest(list: &[T]) -> T { 2 | // types like i32 and char that have a known size can be stored on the stack, so they implement the Copy trait 3 | let mut largest = list[0]; 4 | for &item in list { 5 | if item > largest { 6 | largest = item; 7 | } 8 | } 9 | largest 10 | } 11 | 12 | pub fn run() { 13 | let number_list = vec![34, 50, 25, 100, 65]; 14 | let result = largest(&number_list); 15 | println!("The largest number is {}", result); 16 | let number_list = vec![102, 34, 6000, 89, 54, 2, 43, 8]; 17 | let result = largest(&number_list); 18 | println!("The largest number is {}", result); 19 | } 20 | -------------------------------------------------------------------------------- /playground/09-generic-types/src/generic_data_types.rs: -------------------------------------------------------------------------------- 1 | // The process of monomorphization makes Rust’s generics extremely efficient at runtime. 2 | 3 | // fn largest(list: &[T]) -> T { 4 | // let mut largest = list[0]; 5 | // for &item in list { 6 | // if item > largest { 7 | // largest = item; 8 | // } 9 | // } 10 | // largest 11 | // } 12 | 13 | // pub fn run() { 14 | // let number_list = vec![34, 50, 25, 100, 65]; 15 | // let result = largest(&number_list); 16 | // println!("The largest number is {}", result); 17 | // let char_list = vec!['y', 'm', 'a', 'q']; 18 | // let result = largest(&char_list); 19 | // println!("The largest char is {}", result); 20 | // } 21 | 22 | // structs 23 | // struct Point { 24 | // x: T, 25 | // y: T, 26 | // } 27 | 28 | // pub fn run() { 29 | // let integer = Point { x: 5, y: 10 }; 30 | // let float = Point { x: 1.0, y: 4.0 }; 31 | // } 32 | 33 | // struct Point { 34 | // x: T, 35 | // y: U, 36 | // } 37 | 38 | // fn main() { 39 | // let integer_and_float = Point { x: 5, y: 4.0 }; 40 | // } 41 | 42 | // enums 43 | enum Option { 44 | Some(T), 45 | None, 46 | } 47 | 48 | enum Result { 49 | Ok(T), 50 | Err(E), 51 | } 52 | 53 | // in method definition 54 | struct Point { 55 | x: T, 56 | y: T, 57 | } 58 | 59 | impl Point { 60 | fn x(&self) -> &T { 61 | &self.x 62 | } 63 | } 64 | 65 | pub fn run() { 66 | let integer = Point { x: 5, y: 10 }; 67 | println!("integer.x = {}", integer.x()); 68 | } 69 | -------------------------------------------------------------------------------- /playground/09-generic-types/src/lifetimes.rs: -------------------------------------------------------------------------------- 1 | fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { 2 | // the lifetime of the reference returned by the longest function 3 | // is the same as the smaller of the lifetimes of the references passed in 4 | if x.len() > y.len() { 5 | x 6 | } else { 7 | y 8 | } 9 | } 10 | 11 | struct ImportantExcerpt<'a> { 12 | part: &'a str, 13 | } 14 | 15 | pub fn run() { 16 | // let x = 5; 17 | // let r = &x; 18 | // println!("r: {}", r); 19 | 20 | // generic lifetimes 21 | // let string1 = String::from("abcd"); 22 | // let string2 = "xyz"; 23 | // let result = longest(string1.as_str(), string2); 24 | // println!("The longest string is {}", result); 25 | 26 | let string1 = String::from("long string is long"); 27 | { 28 | let string2 = String::from("xyz"); 29 | let result = longest(string1.as_str(), string2.as_str()); 30 | println!("The longest string is {}", result); 31 | } 32 | 33 | let novel = String::from("Call me Ishmael. Some years ago..."); 34 | let first_sentence = novel.split('.').next().expect("Could not find a '.'"); 35 | let i = ImportantExcerpt { 36 | part: first_sentence, 37 | }; 38 | 39 | let s: &'static str = "I have a static lifetime."; 40 | } 41 | -------------------------------------------------------------------------------- /playground/09-generic-types/src/main.rs: -------------------------------------------------------------------------------- 1 | // mod duplicate; 2 | // mod generic_data_types; 3 | // mod traits; 4 | mod lifetimes; 5 | 6 | fn main() { 7 | // duplicate::run(); 8 | // generic_data_types::run(); 9 | // traits::run(); 10 | lifetimes::run(); 11 | } 12 | -------------------------------------------------------------------------------- /playground/09-generic-types/src/traits.rs: -------------------------------------------------------------------------------- 1 | pub struct NewsArticle { 2 | pub headline: String, 3 | pub location: String, 4 | pub author: String, 5 | pub content: String, 6 | } 7 | 8 | pub trait Summary { 9 | // fn summarize(&self) -> String; 10 | // fn summarize(&self) -> String { 11 | // String::from("(Read more...)") 12 | // } 13 | fn summarize_author(&self) -> String; 14 | fn summarize(&self) -> String { 15 | format!("(Read more from {}...)", self.summarize_author()) 16 | } 17 | } 18 | 19 | // impl Summary for NewsArticle { 20 | // fn summarize(&self) -> String { 21 | // format!("{}, by {} ({})", self.headline, self.author, self.location) 22 | // } 23 | // } 24 | 25 | // impl Summary for NewsArticle {} // to display (Read more...) 26 | 27 | pub struct Tweet { 28 | pub username: String, 29 | pub content: String, 30 | pub reply: bool, 31 | pub retweet: bool, 32 | } 33 | 34 | impl Summary for Tweet { 35 | // fn summarize(&self) -> String { 36 | // format!("{}: {}", self.username, self.content) 37 | // } 38 | fn summarize_author(&self) -> String { 39 | format!("@{}", self.username) 40 | } 41 | } 42 | 43 | pub fn run() { 44 | let tweet = Tweet { 45 | username: String::from("horse_ebooks"), 46 | content: String::from("of course, as you probably already know, people"), 47 | reply: false, 48 | retweet: false, 49 | }; 50 | println!("1 new tweet: {}", tweet.summarize()); 51 | 52 | // let article = NewsArticle { 53 | // headline: String::from("Penguins win the Stanley Cup Championship!"), 54 | // location: String::from("Pittsburgh, PA, USA"), 55 | // author: String::from("Iceburgh"), 56 | // content: String::from( 57 | // "The Pittsburgh Penguins once again are the best \ 58 | // hockey team in the NHL.", 59 | // ), 60 | // }; 61 | 62 | // println!("New article available! {}", article.summarize()); 63 | } 64 | 65 | // trait as parameter 66 | // pub fn notify(item: &impl Summary) { 67 | // println!("Breaking news! {}", item.summarize()); 68 | // } 69 | 70 | // trait bound syntax 71 | // pub fn notify(item: &T) { 72 | // println!("Breaking news! {}", item.summarize()); 73 | // } 74 | 75 | // pub fn notify(item1: &T, item2: &T) {} 76 | 77 | // multiple traits with + syntax 78 | // pub fn notify(item: &(impl Summary + Display)) {} 79 | // pub fn notify(item: &T) {} 80 | 81 | // where clause 82 | // fn some_function(t: &T, u: &U) -> i32 {} 83 | // fn some_function(t: &T, u: &U) -> i32 where T: Display + Clone, 84 | // U: Clone + Debug { 85 | // } 86 | 87 | // returning types that implement traits 88 | // fn returns_summarizable() -> impl Summary { 89 | // Tweet { 90 | // username: String::from("horse_ebooks"), 91 | // content: String::from("of course, as you..."), 92 | // reply: false, 93 | // retweet: false 94 | // } 95 | // } 96 | -------------------------------------------------------------------------------- /playground/10-automated-tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "automated_tests" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /playground/10-automated-tests/src/lib.rs: -------------------------------------------------------------------------------- 1 | // cargo new 10-automated-tests --name=automated_tests --lib 2 | // cargo test 3 | // cargo test -- --test-threads=1 // will take longer, but the tests won't interfere with each other 4 | // cargo test -- --show-output // see printed values for passing tests as well 5 | // cargo test one_hundred // pass the name of any test function to cargo test to run only that test 6 | // cargo test add // Filtering to Run Multiple Tests, 2 tests are prefixed with add 7 | // cargo test -- --ignored // run only the ignored test 8 | 9 | // #[derive(Debug)] 10 | // struct Rectangle { 11 | // width: u32, 12 | // height: u32, 13 | // } 14 | 15 | // impl Rectangle { 16 | // fn can_hold(&self, other: &Rectangle) -> bool { 17 | // self.width > other.width && self.height > other.height 18 | // // self.width < other.width && self.height > other.height 19 | // } 20 | // } 21 | 22 | pub fn add_two(a: i32) -> i32 { 23 | // a + 2 24 | // a + 3 25 | internal_adder(a, 2) 26 | } 27 | 28 | fn internal_adder(a: i32, b: i32) -> i32 { 29 | a + b 30 | } 31 | 32 | // pub fn greeting(name: &str) -> String { 33 | // // format!("Hello {}!", name) 34 | // String::from("Hello!") 35 | // } 36 | 37 | // pub struct Guess { 38 | // value: i32, 39 | // } 40 | 41 | // impl Guess { 42 | // pub fn new(value: i32) -> Guess { 43 | // // if value < 1 || value > 100 { 44 | // // if value < 1 { 45 | // // // test did not panic as expected 46 | // // panic!( 47 | // // "Guess value must be greater than or equal to 1, got {}.", 48 | // // value 49 | // // ); 50 | // // } else if value > 100 { 51 | // // panic!( 52 | // // "Guess value must be less than or equal to 100, got {}.", 53 | // // value 54 | // // ); 55 | // // } 56 | // if value < 1 { 57 | // panic!( 58 | // "Guess value must be less than or equal to 100, got {}.", 59 | // value 60 | // ); 61 | // } else if value > 100 { 62 | // panic!( 63 | // "Guess value must be greater than or equal to 1, got {}.", 64 | // value 65 | // ); 66 | // } 67 | // Guess { value } 68 | // } 69 | // } 70 | 71 | #[cfg(test)] // the configuration option is test 72 | mod tests { 73 | // #[test] 74 | // fn exploration() { 75 | // let result = 2 + 2; 76 | // assert_eq!(result, 4); 77 | // } 78 | 79 | // #[test] 80 | // fn another() { 81 | // panic!("Make this test fail"); 82 | // } 83 | 84 | use super::*; // anything we define in the outer module is available to this tests module 85 | 86 | // Checking Results with the assert! Macro 87 | // #[test] 88 | // fn larger_can_hold_smaller() { 89 | // let larger = Rectangle { 90 | // width: 8, 91 | // height: 7, 92 | // }; 93 | // let smaller = Rectangle { 94 | // width: 5, 95 | // height: 1, 96 | // }; 97 | // assert!(larger.can_hold(&smaller)); 98 | // } 99 | 100 | // #[test] 101 | // fn smaller_cannot_hold_larger() { 102 | // let larger = Rectangle { 103 | // width: 8, 104 | // height: 7, 105 | // }; 106 | // let smaller = Rectangle { 107 | // width: 5, 108 | // height: 1, 109 | // }; 110 | // assert!(!smaller.can_hold(&larger)); 111 | // } 112 | 113 | // Testing Equality with the assert_eq! and assert_ne! Macros 114 | // #[test] 115 | // fn it_adds_two() { 116 | // // assert_eq!(4, add_two(2)); // 'assertion failed: `(left == right)` left: `4`, right: `5`' 117 | // assert_ne!(4, add_two(2)); 118 | // } 119 | 120 | // Adding Custom Failure Messages 121 | // #[test] 122 | // fn greeting_contains_name() { 123 | // let result = greeting("Carol"); 124 | // // assert!(result.contains("Carol")); 125 | // assert!( 126 | // result.contains("Carol"), 127 | // "Greeting did not contain name, value was `{}`", // Hello! 128 | // result 129 | // ); 130 | // } 131 | 132 | // Checking for Panics with should_panic 133 | // #[test] 134 | // // #[should_panic] 135 | // #[should_panic(expected = "Guess value must be less than or equal to 100")] 136 | // // a substring is enough 137 | // // we could have write Guess value must be less than or equal to 100, got 200. 138 | // // panic message: `"Guess value must be greater than or equal to 1, got 200."`, 139 | // // expected substring: `"Guess value must be less than or equal to 100"` 140 | // fn greater_than_100() { 141 | // Guess::new(200); 142 | // } 143 | 144 | // Using Result in Tests 145 | // #[test] 146 | // fn it_works() -> Result<(), String> { 147 | // if 2 + 2 == 4 { 148 | // Ok(()) 149 | // } else { 150 | // Err(String::from("two plus two does not equal four")) 151 | // } 152 | // } 153 | 154 | // Running Single Tests 155 | // #[test] 156 | // fn add_two_and_two() { 157 | // assert_eq!(4, add_two(2)); 158 | // } 159 | 160 | // #[test] 161 | // fn add_three_and_two() { 162 | // assert_eq!(5, add_two(3)); 163 | // } 164 | 165 | // #[test] 166 | // fn one_hundred() { 167 | // assert_eq!(102, add_two(100)); 168 | // } 169 | 170 | // // Ignoring Some Tests Unless Specifically Requested 171 | // #[test] 172 | // #[ignore] 173 | // fn expensive_test() { 174 | // // code that takes an hour to run 175 | // } 176 | 177 | // Testing a private function is permitted by Rust 178 | #[test] 179 | fn internal() { 180 | assert_eq!(4, internal_adder(2, 2)); 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /playground/10-automated-tests/tests/common/mod.rs: -------------------------------------------------------------------------------- 1 | // helpers 2 | 3 | pub fn setup() { 4 | // setup code specific to your library's tests would go here 5 | } 6 | -------------------------------------------------------------------------------- /playground/10-automated-tests/tests/integration_test.rs: -------------------------------------------------------------------------------- 1 | // cargo test --test integration_test // run integration test only 2 | 3 | use automated_tests; // each file in the tests directory is a separate crate, so we need to bring our library 4 | // a lib.rs file is necessary to import the crate (we can't import functions from main.rs) 5 | mod common; 6 | 7 | #[test] 8 | fn it_adds_two() { 9 | common::setup(); 10 | assert_eq!(4, automated_tests::add_two(2)); 11 | } 12 | -------------------------------------------------------------------------------- /playground/11-iterators-closures/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "iterators_closures" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /playground/11-iterators-closures/src/closures.rs: -------------------------------------------------------------------------------- 1 | use std::thread; 2 | use std::time::Duration; 3 | 4 | // fn simulated_expensive_calculation(intensity: u32) -> u32 { 5 | // println!("calculating slowly..."); 6 | // thread::sleep(Duration::from_secs(2)); 7 | // intensity 8 | // } 9 | 10 | // Closure Type Inference and Annotation 11 | // fn add_one_v1 (x: u32) -> u32 { x + 1 } 12 | // let add_one_v2 = |x: u32| -> u32 { x + 1 }; 13 | // let add_one_v3 = |x| { x + 1 }; 14 | // let add_one_v4 = |x| x + 1 ; 15 | 16 | // Storing Closures Using Generic Parameters and the Fn Traits 17 | struct Cacher 18 | where 19 | T: Fn(u32) -> u32, 20 | { 21 | calculation: T, 22 | value: Option, 23 | } 24 | 25 | impl Cacher 26 | where 27 | T: Fn(u32) -> u32, 28 | { 29 | fn new(calculation: T) -> Cacher { 30 | Cacher { 31 | calculation, 32 | value: None, 33 | } 34 | } 35 | 36 | fn value(&mut self, arg: u32) -> u32 { 37 | match self.value { 38 | Some(v) => v, 39 | None => { 40 | let v = (self.calculation)(arg); 41 | self.value = Some(v); 42 | v 43 | } 44 | } 45 | } 46 | } 47 | 48 | fn generate_workout(intensity: u32, random_number: u32) { 49 | // let expensive_result = simulated_expensive_calculation(intensity); 50 | // let expensive_closure = |num: u32| -> u32 { 51 | // let expensive_closure = |num| { 52 | // println!("calculating slowly..."); 53 | // thread::sleep(Duration::from_secs(2)); 54 | // num 55 | // }; 56 | let mut expensive_result = Cacher::new(|num| { 57 | println!("calculating slowly..."); 58 | thread::sleep(Duration::from_secs(2)); 59 | num 60 | }); 61 | if intensity < 25 { 62 | println!("Today, do {} pushups!", expensive_result.value(intensity)); 63 | println!("Next, do {} situps!", expensive_result.value(intensity)); 64 | } else { 65 | if random_number == 3 { 66 | println!("Take a break today! Remember to stay hydrated!"); 67 | } else { 68 | println!( 69 | "Today, run for {} minutes!", 70 | expensive_result.value(intensity) 71 | ); 72 | } 73 | } 74 | } 75 | 76 | pub fn run() { 77 | // let simulated_user_specified_value = 10; 78 | // let simulated_random_number = 7; 79 | // generate_workout(simulated_user_specified_value, simulated_random_number); 80 | let x = vec![1, 2, 3]; 81 | let equal_to_x = move |z| z == x; 82 | // println!("can't use x here {:?}", x); // The x value is moved into the closure, because we added the move keyword 83 | let y = vec![1, 2, 3]; 84 | assert!(equal_to_x(y)); 85 | } 86 | 87 | // Most of the time when specifying one of the Fn trait bounds, 88 | // you can start with Fn and the compiler will tell you 89 | // if you need FnMut or FnOnce based on what happens in the closure body. 90 | -------------------------------------------------------------------------------- /playground/11-iterators-closures/src/iterators.rs: -------------------------------------------------------------------------------- 1 | pub fn run() { 2 | let v1 = vec![1, 2, 3]; 3 | let v1_iter = v1.iter(); 4 | for val in v1_iter { 5 | println!("Got: {}", val); 6 | } 7 | 8 | // into_iter takes ownership 9 | // iter_mut to iterate over mutable references 10 | 11 | // map method 12 | let v2: Vec<_> = v1.iter().map(|x| x + 1).collect(); 13 | assert_eq!(v2, vec![2, 3, 4]); 14 | } 15 | -------------------------------------------------------------------------------- /playground/11-iterators-closures/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[derive(PartialEq, Debug)] 2 | 3 | struct Shoe { 4 | size: u32, 5 | style: String, 6 | } 7 | 8 | fn shoes_in_size(shoes: Vec, shoe_size: u32) -> Vec { 9 | // filter method 10 | shoes.into_iter().filter(|s| s.size == shoe_size).collect() 11 | } 12 | 13 | // custom iterator 14 | struct Counter { 15 | count: u32, 16 | } 17 | 18 | impl Counter { 19 | fn new() -> Counter { 20 | Counter { count: 0 } 21 | } 22 | } 23 | 24 | impl Iterator for Counter { 25 | type Item = u32; 26 | 27 | fn next(&mut self) -> Option { 28 | if self.count < 5 { 29 | self.count += 1; 30 | Some(self.count) 31 | } else { 32 | None 33 | } 34 | } 35 | } 36 | 37 | #[cfg(test)] 38 | mod tests { 39 | use super::*; 40 | 41 | // #[test] 42 | // fn iterator_demonstration() { 43 | // let v1 = vec![1, 2, 3]; 44 | // let mut v1_iter = v1.iter(); 45 | // assert_eq!(v1_iter.next(), Some(&1)); 46 | // assert_eq!(v1_iter.next(), Some(&2)); 47 | // assert_eq!(v1_iter.next(), Some(&3)); 48 | // assert_eq!(v1_iter.next(), None); 49 | // } 50 | 51 | // #[test] 52 | // fn iterator_sum() { 53 | // let v1 = vec![1, 2, 3]; 54 | // let v1_iter = v1.iter(); 55 | // let total: i32 = v1_iter.sum(); 56 | // assert_eq!(total, 6); 57 | // } 58 | 59 | #[test] 60 | fn filters_by_size() { 61 | let shoes = vec![ 62 | Shoe { 63 | size: 10, 64 | style: String::from("sneaker"), 65 | }, 66 | Shoe { 67 | size: 13, 68 | style: String::from("sandal"), 69 | }, 70 | Shoe { 71 | size: 10, 72 | style: String::from("boot"), 73 | }, 74 | ]; 75 | let in_my_size = shoes_in_size(shoes, 10); 76 | assert_eq!( 77 | in_my_size, 78 | vec![ 79 | Shoe { 80 | size: 10, 81 | style: String::from("sneaker"), 82 | }, 83 | Shoe { 84 | size: 10, 85 | style: String::from("boot"), 86 | }, 87 | ] 88 | ) 89 | } 90 | 91 | #[test] 92 | fn calling_next_directly() { 93 | let mut counter = Counter::new(); 94 | assert_eq!(counter.next(), Some(1)); 95 | assert_eq!(counter.next(), Some(2)); 96 | assert_eq!(counter.next(), Some(3)); 97 | assert_eq!(counter.next(), Some(4)); 98 | assert_eq!(counter.next(), Some(5)); 99 | assert_eq!(counter.next(), None); 100 | } 101 | 102 | #[test] 103 | fn using_other_iterator_trait_methods() { 104 | let sum: u32 = Counter::new() 105 | .zip(Counter::new().skip(1)) 106 | .map(|(a, b)| a * b) 107 | .filter(|x| x % 3 == 0) 108 | .sum(); 109 | assert_eq!(18, sum); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /playground/11-iterators-closures/src/main.rs: -------------------------------------------------------------------------------- 1 | // mod closures; 2 | mod iterators; 3 | 4 | fn main() { 5 | // closures::run(); 6 | iterators::run(); 7 | } 8 | -------------------------------------------------------------------------------- /playground/12-more-cargo-crates/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cargo_crates" 3 | version = "0.1.0" 4 | edition = "2021" 5 | description = "A fun game where you guess what number the computer has chosen." 6 | license = "MIT OR Apache-2.0" 7 | 8 | # cargo publish 9 | # cargo yank --vers 1.0.1 to remove a version 10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 11 | 12 | [dependencies] 13 | 14 | [profile.dev] 15 | # opt-level = 0 # by default 16 | opt-level = 1 # override 17 | 18 | [profile.release] 19 | opt-level = 3 # by default 20 | -------------------------------------------------------------------------------- /playground/12-more-cargo-crates/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Art 2 | //! 3 | //! A library for modeling artistic concepts. 4 | 5 | pub use self::kinds::PrimaryColor; 6 | pub use self::kinds::SecondaryColor; 7 | pub use self::utils::mix; 8 | 9 | pub mod kinds { 10 | /// The primary colors according to the RYB color model. 11 | pub enum PrimaryColor { 12 | Red, 13 | Yellow, 14 | Blue, 15 | } 16 | 17 | /// The secondary colors according to the RYB color model. 18 | pub enum SecondaryColor { 19 | Orange, 20 | Green, 21 | Purple, 22 | } 23 | } 24 | 25 | pub mod utils { 26 | use crate::kinds::*; 27 | 28 | /// Combines two primary colors in equal amounts to create 29 | /// a secondary color. 30 | pub fn mix(c1: PrimaryColor, c2: PrimaryColor) -> SecondaryColor { 31 | // --snip-- 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /playground/12-more-cargo-crates/src/lib_example.rs: -------------------------------------------------------------------------------- 1 | //! # Cargo Crates 2 | //! 3 | //! `cargo_crates` is a collection of utilities to make performing certain 4 | //! calculations more convenient. 5 | 6 | /// Adds one to the number given. 7 | /// 8 | /// # Examples 9 | /// 10 | /// ``` 11 | /// let arg = 5; 12 | /// let answer = cargo_crates::add_one(arg); 13 | /// 14 | /// assert_eq!(6, answer); 15 | /// ``` 16 | pub fn add_one(x: i32) -> i32 { 17 | x + 1 18 | } 19 | 20 | // cargo doc --open 21 | // cargo test // run the code examples 22 | -------------------------------------------------------------------------------- /playground/12-more-cargo-crates/src/main.rs: -------------------------------------------------------------------------------- 1 | // use cargo_crates::kinds::PrimaryColor; 2 | use cargo_crates::PrimaryColor; 3 | // use cargo_crates::utils::mix; 4 | use cargo_crates::mix; 5 | 6 | fn main() { 7 | let red = PrimaryColor::Red; 8 | let yellow = PrimaryColor::Yellow; 9 | mix(red, yellow); 10 | } 11 | -------------------------------------------------------------------------------- /playground/13-cargo-workspace/Cargo.toml: -------------------------------------------------------------------------------- 1 | # cargo install ripgrep # to install a binary globally 2 | # cargo --list # see all the available commands 3 | 4 | [workspace] 5 | 6 | members = [ 7 | "adder", 8 | "add_one", 9 | ] -------------------------------------------------------------------------------- /playground/13-cargo-workspace/add_one/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "add_one" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | rand = "0.8.3" 10 | -------------------------------------------------------------------------------- /playground/13-cargo-workspace/add_one/src/lib.rs: -------------------------------------------------------------------------------- 1 | use rand; 2 | 3 | pub fn add_one(x: i32) -> i32 { 4 | x + 1 5 | } 6 | 7 | // cargo test -p add-one 8 | #[cfg(test)] 9 | mod tests { 10 | use super::*; 11 | 12 | #[test] 13 | fn it_works() { 14 | assert_eq!(3, add_one(2)); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /playground/13-cargo-workspace/adder/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "adder" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | rand = "0.8.3" 10 | add_one = { path = "../add_one" } -------------------------------------------------------------------------------- /playground/13-cargo-workspace/adder/src/main.rs: -------------------------------------------------------------------------------- 1 | // cargo run -p adder 2 | use add_one; 3 | use rand; 4 | 5 | fn main() { 6 | let num = 10; 7 | println!( 8 | "Hello, world! {} plus one is {}!", 9 | num, 10 | add_one::add_one(num) 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /playground/14-smart-pointers/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pointers" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /playground/14-smart-pointers/src/boxes.rs: -------------------------------------------------------------------------------- 1 | enum List { 2 | Cons(i32, Box), 3 | Nil, 4 | } 5 | 6 | use self::List::{Cons, Nil}; 7 | 8 | pub fn run() { 9 | let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil)))))); 10 | // Boxes provide the indirection and heap allocation 11 | } 12 | -------------------------------------------------------------------------------- /playground/14-smart-pointers/src/dereference.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Deref; 2 | 3 | pub fn run() { 4 | let x = 5; 5 | // let y = &x; 6 | // let y = Box::new(x); 7 | // let y = MyBox::new(x); 8 | // assert_eq!(5, x); 9 | // assert_eq!(5, *y); // follow the reference to the value it’s pointing to (hence dereference) 10 | let m = MyBox::new(String::from("Rust")); 11 | hello(&m); 12 | } 13 | 14 | // Defining Our Own Smart Pointer 15 | struct MyBox(T); 16 | 17 | impl MyBox { 18 | fn new(x: T) -> MyBox { 19 | MyBox(x) 20 | } 21 | } 22 | 23 | // Treating a Type Like a Reference by Implementing the Deref Trait 24 | impl Deref for MyBox { 25 | type Target = T; 26 | 27 | fn deref(&self) -> &Self::Target { 28 | &self.0 29 | } 30 | } 31 | 32 | // Implicit Deref Coercions with Functions and Methods 33 | fn hello(name: &str) { 34 | println!("Hello, {}!", name); 35 | } 36 | -------------------------------------------------------------------------------- /playground/14-smart-pointers/src/drop.rs: -------------------------------------------------------------------------------- 1 | struct CustomSmartPointer { 2 | data: String, 3 | } 4 | 5 | impl Drop for CustomSmartPointer { 6 | fn drop(&mut self) { 7 | println!("Dropping CustomSmartPointer with data `{}`!", self.data); 8 | } 9 | } 10 | 11 | pub fn run() { 12 | let c = CustomSmartPointer { 13 | data: String::from("my stuff"), 14 | }; 15 | let d = CustomSmartPointer { 16 | data: String::from("other stuff"), 17 | }; 18 | println!("CustomSmartPointers created: {} and {}.", c.data, d.data); 19 | // c.drop(); 20 | drop(c); // call std::mem::drop to explicitly drop a value before it goes out of scope 21 | println!("CustomSmartPointer dropped before the end of main."); 22 | } 23 | -------------------------------------------------------------------------------- /playground/14-smart-pointers/src/lib.rs: -------------------------------------------------------------------------------- 1 | // interior mutability problem 2 | pub trait Messenger { 3 | fn send(&self, msg: &str); 4 | } 5 | 6 | pub struct LimitTracker<'a, T: Messenger> { 7 | messenger: &'a T, 8 | value: usize, 9 | max: usize, 10 | } 11 | 12 | impl<'a, T> LimitTracker<'a, T> 13 | where 14 | T: Messenger, 15 | { 16 | pub fn new(messenger: &T, max: usize) -> LimitTracker { 17 | LimitTracker { 18 | messenger, 19 | value: 0, 20 | max, 21 | } 22 | } 23 | 24 | pub fn set_value(&mut self, value: usize) { 25 | self.value = value; 26 | 27 | let percentage_of_max = self.value as f64 / self.max as f64; 28 | 29 | if percentage_of_max >= 1.0 { 30 | self.messenger.send("Error: You are over your quota!"); 31 | } else if percentage_of_max >= 0.9 { 32 | self.messenger 33 | .send("Urgent warning: You've used up over 90% of your quota!"); 34 | } else if percentage_of_max >= 0.75 { 35 | self.messenger 36 | .send("Urgent warning: You've used up over 75% of your quota!"); 37 | } 38 | } 39 | } 40 | 41 | #[cfg(test)] 42 | mod tests { 43 | use super::*; 44 | use std::cell::RefCell; 45 | 46 | struct MockMessenger { 47 | sent_messages: RefCell>, 48 | } 49 | 50 | impl MockMessenger { 51 | fn new() -> MockMessenger { 52 | MockMessenger { 53 | sent_messages: RefCell::new(vec![]), 54 | } 55 | } 56 | } 57 | 58 | impl Messenger for MockMessenger { 59 | fn send(&self, message: &str) { 60 | self.sent_messages.borrow_mut().push(String::from(message)); // get a mutable reference to the vector inside the RefCell> 61 | } 62 | } 63 | 64 | #[test] 65 | fn it_sends_an_over_75_percent_warning_message() { 66 | let mock_messenger = MockMessenger::new(); 67 | let mut limit_tracker = LimitTracker::new(&mock_messenger, 100); 68 | limit_tracker.set_value(80); 69 | assert_eq!(mock_messenger.sent_messages.borrow().len(), 1); // get an immutable reference to the vector 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /playground/14-smart-pointers/src/main.rs: -------------------------------------------------------------------------------- 1 | // mod boxes; 2 | // mod dereference; 3 | // mod drop; 4 | // mod ref_counting; 5 | mod ref_cycles; 6 | 7 | fn main() { 8 | // boxes::run(); 9 | // dereference::run(); 10 | // drop::run(); 11 | // ref_counting::run(); 12 | ref_cycles::run(); 13 | } 14 | -------------------------------------------------------------------------------- /playground/14-smart-pointers/src/ref_counting.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | 3 | enum List { 4 | // Cons(i32, Rc), 5 | Cons(Rc>, Rc), // By using RefCell, we have an outwardly immutable List value. 6 | Nil, 7 | } 8 | 9 | use self::List::{Cons, Nil}; 10 | use std::cell::RefCell; 11 | use std::rc::Rc; 12 | 13 | pub fn run() { 14 | // let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil))))); 15 | // println!("count after creating a = {}", Rc::strong_count(&a)); // 1 16 | // let b = Cons(3, Rc::clone(&a)); 17 | // println!("count after creating b = {}", Rc::strong_count(&a)); // 2 18 | // { 19 | // let c = Cons(4, Rc::clone(&a)); 20 | // println!("count after creating c = {}", Rc::strong_count(&a)); // 3 21 | // } 22 | // println!("count after c goes out of scope = {}", Rc::strong_count(&a)); // 2 23 | 24 | // Having Multiple Owners of Mutable Data by Combining Rc and RefCell 25 | let value = Rc::new(RefCell::new(5)); 26 | 27 | let a = Rc::new(Cons(Rc::clone(&value), Rc::new(Nil))); 28 | 29 | let b = Cons(Rc::new(RefCell::new(3)), Rc::clone(&a)); 30 | let c = Cons(Rc::new(RefCell::new(4)), Rc::clone(&a)); 31 | 32 | *value.borrow_mut() += 10; 33 | 34 | println!("a after = {:?}", a); // a after = Cons(RefCell { value: 15 }, Nil) 35 | println!("b after = {:?}", b); // b after = Cons(RefCell { value: 3 }, Cons(RefCell { value: 15 }, Nil)) 36 | println!("c after = {:?}", c); // c after = Cons(RefCell { value: 4 }, Cons(RefCell { value: 15 }, Nil)) 37 | } 38 | -------------------------------------------------------------------------------- /playground/14-smart-pointers/src/ref_cycles.rs: -------------------------------------------------------------------------------- 1 | // use self::List::{Cons, Nil}; 2 | use std::cell::RefCell; 3 | use std::rc::{Rc, Weak}; 4 | 5 | #[derive(Debug)] 6 | 7 | // enum List { 8 | // Cons(i32, RefCell>), 9 | // Nil, 10 | // } 11 | 12 | // impl List { 13 | // fn tail(&self) -> Option<&RefCell>> { 14 | // match self { 15 | // Cons(_, item) => Some(item), 16 | // Nil => None, 17 | // } 18 | // } 19 | // } 20 | 21 | // pub fn run() { 22 | // let a = Rc::new(Cons(5, RefCell::new(Rc::new(Nil)))); 23 | // println!("a initial rc count = {}", Rc::strong_count(&a)); 24 | // println!("a next item = {:?}", a.tail()); 25 | 26 | // let b = Rc::new(Cons(10, RefCell::new(Rc::clone(&a)))); 27 | // println!("a rc count after b creation = {}", Rc::strong_count(&a)); 28 | // println!("b initial rc count = {}", Rc::strong_count(&b)); 29 | // println!("b next item = {:?}", b.tail()); 30 | 31 | // // We modify a so it points to b instead of Nil, creating a cycle 32 | // if let Some(link) = a.tail() { 33 | // *link.borrow_mut() = Rc::clone(&b); 34 | // } 35 | 36 | // println!("b rc count after changing a = {}", Rc::strong_count(&b)); 37 | // println!("a rc count after changing a = {}", Rc::strong_count(&a)); 38 | 39 | // // Uncomment the next line to see that we have a cycle; 40 | // // Rust will try to print this cycle with a pointing to b pointing to a and so forth until it overflows the stack 41 | // // println!("a next item = {:?}", a.tail()); 42 | // } 43 | 44 | struct Node { 45 | value: i32, 46 | parent: RefCell>, 47 | children: RefCell>>, 48 | } 49 | 50 | pub fn run() { 51 | let leaf = Rc::new(Node { 52 | value: 3, 53 | parent: RefCell::new(Weak::new()), 54 | children: RefCell::new(vec![]), 55 | }); 56 | // println!("leaf parent = {:?}", leaf.parent.borrow().upgrade()); // leaf parent = None 57 | println!( 58 | "leaf strong = {}, weak = {}", 59 | Rc::strong_count(&leaf), 60 | Rc::weak_count(&leaf), 61 | ); // leaf strong = 1, weak = 0 62 | 63 | { 64 | let branch = Rc::new(Node { 65 | value: 5, 66 | parent: RefCell::new(Weak::new()), 67 | children: RefCell::new(vec![Rc::clone(&leaf)]), 68 | }); 69 | *leaf.parent.borrow_mut() = Rc::downgrade(&branch); 70 | println!( 71 | "branch strong = {}, weak = {}", 72 | Rc::strong_count(&branch), 73 | Rc::weak_count(&branch), 74 | ); // branch strong = 1, weak = 1 (for leaf.parent pointing to branch with a Weak) 75 | 76 | println!( 77 | "leaf strong = {}, weak = {}", 78 | Rc::strong_count(&leaf), 79 | Rc::weak_count(&leaf), 80 | ); // leaf strong = 2, weak = 0 (branch now has a clone of the Rc of leaf stored in branch.children) 81 | } 82 | println!("leaf parent = {:?}", leaf.parent.borrow().upgrade()); // leaf parent = None, branch is out of scope // we don’t get any memory leaks! 83 | println!( 84 | "leaf strong = {}, weak = {}", 85 | Rc::strong_count(&leaf), 86 | Rc::weak_count(&leaf), 87 | ); // leaf strong = 1, weak = 0 88 | } 89 | -------------------------------------------------------------------------------- /playground/15-concurrency/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "concurrency" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /playground/15-concurrency/src/channels.rs: -------------------------------------------------------------------------------- 1 | // Go: “Do not communicate by sharing memory; instead, share memory by communicating.” 2 | // mpsc stands for multiple producer, single consumer 3 | use std::sync::mpsc; 4 | use std::thread; 5 | use std::time::Duration; 6 | 7 | pub fn run() { 8 | let (tx, rx) = mpsc::channel(); // transmitter, receiver 9 | 10 | // thread::spawn(move || { 11 | // let val = String::from("hi"); 12 | // tx.send(val).unwrap(); // unwrap to panic in case of an error // ownership of val is transferred 13 | // }); 14 | // let received = rx.recv().unwrap(); 15 | // println!("Got: {}", received); 16 | 17 | // Sending Multiple Values and Seeing the Receiver Waiting 18 | // thread::spawn(move || { 19 | // let vals = vec![ 20 | // String::from("hi"), 21 | // String::from("from"), 22 | // String::from("the"), 23 | // String::from("thread"), 24 | // ]; 25 | // for val in vals { 26 | // tx.send(val).unwrap(); 27 | // thread::sleep(Duration::from_secs(1)); 28 | // } 29 | // }); 30 | 31 | // for received in rx { 32 | // println!("Got: {}", received); 33 | // } 34 | 35 | // Creating Multiple Producers by Cloning the Transmitter 36 | let tx1 = tx.clone(); 37 | thread::spawn(move || { 38 | let vals = vec![ 39 | String::from("hi"), 40 | String::from("from"), 41 | String::from("the"), 42 | String::from("thread"), 43 | ]; 44 | for val in vals { 45 | tx1.send(val).unwrap(); 46 | thread::sleep(Duration::from_secs(1)); 47 | } 48 | }); 49 | 50 | thread::spawn(move || { 51 | let vals = vec![ 52 | String::from("more"), 53 | String::from("messages"), 54 | String::from("for"), 55 | String::from("you"), 56 | ]; 57 | for val in vals { 58 | tx.send(val).unwrap(); 59 | thread::sleep(Duration::from_secs(1)); 60 | } 61 | }); 62 | 63 | for received in rx { 64 | println!("Got: {}", received); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /playground/15-concurrency/src/main.rs: -------------------------------------------------------------------------------- 1 | // mod threads; 2 | // mod channels; 3 | mod shared_state; 4 | 5 | fn main() { 6 | // threads::run(); 7 | // channels::run(); 8 | shared_state::run(); 9 | } 10 | -------------------------------------------------------------------------------- /playground/15-concurrency/src/shared_state.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; // Arc stands for atomically reference counted, safe to share across threads vs Rc (which is not Send nor Sync) 2 | use std::thread; 3 | 4 | pub fn run() { 5 | // let m = Mutex::new(5); 6 | // { 7 | // let mut num = m.lock().unwrap(); 8 | // *num = 6; 9 | // } 10 | // println!("m = {:?}", m); // m = Mutex { data: 6, poisoned: false, .. } 11 | 12 | // Sharing a Mutex Between Multiple Threads 13 | let counter = Arc::new(Mutex::new(0)); 14 | let mut handles = vec![]; 15 | for _ in 0..10 { 16 | let counter = Arc::clone(&counter); 17 | let handle = thread::spawn(move || { 18 | let mut num = counter.lock().unwrap(); 19 | *num += 1; 20 | }); 21 | handles.push(handle); 22 | } 23 | for handle in handles { 24 | handle.join().unwrap(); 25 | } 26 | println!("Result: {}", *counter.lock().unwrap()); // 10 27 | } 28 | -------------------------------------------------------------------------------- /playground/15-concurrency/src/threads.rs: -------------------------------------------------------------------------------- 1 | use std::thread; 2 | // use std::time::Duration; 3 | 4 | // pub fn run() { 5 | // let handle = thread::spawn(|| { 6 | // for i in 1..10 { 7 | // println!("hi number {} from the spawned thread!", i); 8 | // thread::sleep(Duration::from_millis(1)); 9 | // } 10 | // }); 11 | // // handle.join().unwrap(); // The main thread will wait for the spawned thread to finish 12 | // for i in 1..5 { 13 | // println!("hi number {} from the main thread!", i); 14 | // thread::sleep(Duration::from_millis(1)); 15 | // } 16 | // handle.join().unwrap(); // Waiting for All Threads to Finish Using join Handles 17 | // } 18 | 19 | // Using move Closures with Threads 20 | pub fn run() { 21 | let v = vec![1, 2, 3]; 22 | let handle = thread::spawn(move || { 23 | // force the closure to take ownership of v 24 | println!("Here's a vector: {:?}", v); 25 | }); 26 | handle.join().unwrap(); 27 | } 28 | -------------------------------------------------------------------------------- /playground/16-oop-features/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "oop" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /playground/16-oop-features/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub struct Post { 2 | // state: Option>, 3 | content: String, 4 | } 5 | 6 | pub struct DraftPost { 7 | content: String, 8 | } 9 | 10 | impl Post { 11 | // pub fn new() -> Post { 12 | // Post { 13 | // state: Some(Box::new(Draft {})), 14 | // content: String::new(), 15 | // } 16 | // } 17 | 18 | // pub fn add_text(&mut self, text: &str) { 19 | // self.content.push_str(text); 20 | // } 21 | 22 | pub fn new() -> DraftPost { 23 | DraftPost { 24 | content: String::new(), 25 | } 26 | } 27 | 28 | pub fn content(&self) -> &str { 29 | // self.state.as_ref().unwrap().content(self) 30 | &self.content 31 | } 32 | 33 | // pub fn request_review(&mut self) { 34 | // if let Some(s) = self.state.take() { 35 | // self.state = Some(s.request_review()) 36 | // } 37 | // } 38 | 39 | // pub fn approve(&mut self) { 40 | // if let Some(s) = self.state.take() { 41 | // self.state = Some(s.approve()) 42 | // } 43 | // } 44 | } 45 | 46 | impl DraftPost { 47 | pub fn add_text(&mut self, text: &str) { 48 | self.content.push_str(text); 49 | } 50 | 51 | pub fn request_review(self) -> PendingReviewPost { 52 | PendingReviewPost { 53 | content: self.content, 54 | } 55 | } 56 | } 57 | 58 | pub struct PendingReviewPost { 59 | content: String, 60 | } 61 | 62 | impl PendingReviewPost { 63 | pub fn approve(self) -> Post { 64 | Post { 65 | content: self.content, 66 | } 67 | } 68 | } 69 | 70 | trait State { 71 | fn request_review(self: Box) -> Box; 72 | fn approve(self: Box) -> Box; 73 | fn content<'a>(&self, post: &'a Post) -> &'a str { 74 | "" 75 | } 76 | } 77 | 78 | struct Draft {} 79 | 80 | impl State for Draft { 81 | fn request_review(self: Box) -> Box { 82 | Box::new(PendingReview {}) 83 | } 84 | fn approve(self: Box) -> Box { 85 | self 86 | } 87 | } 88 | 89 | struct PendingReview {} 90 | 91 | impl State for PendingReview { 92 | fn request_review(self: Box) -> Box { 93 | self 94 | } 95 | fn approve(self: Box) -> Box { 96 | Box::new(Published {}) 97 | } 98 | } 99 | 100 | struct Published {} 101 | 102 | impl State for Published { 103 | fn request_review(self: Box) -> Box { 104 | self 105 | } 106 | fn approve(self: Box) -> Box { 107 | self 108 | } 109 | fn content<'a>(&self, post: &'a Post) -> &'a str { 110 | &post.content 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /playground/16-oop-features/src/lib_basics.rs: -------------------------------------------------------------------------------- 1 | pub struct AveragedCollection { 2 | list: Vec, 3 | average: f64, 4 | } 5 | 6 | // Encapsulation that Hides Implementation Details 7 | impl AveragedCollection { 8 | pub fn add(&mut self, value: i32) { 9 | self.list.push(value); 10 | self.update_average(); 11 | } 12 | 13 | pub fn remove(&mut self) -> Option { 14 | let result = self.list.pop(); 15 | match result { 16 | Some(value) => { 17 | self.update_average(); 18 | Some(value) 19 | } 20 | None => None, 21 | } 22 | } 23 | 24 | pub fn average(&self) -> f64 { 25 | self.average 26 | } 27 | 28 | fn update_average(&mut self) { 29 | let total: i32 = self.list.iter().sum(); 30 | self.average = total as f64 / self.list.len() as f64; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /playground/16-oop-features/src/lib_traits.rs: -------------------------------------------------------------------------------- 1 | pub trait Draw { 2 | fn draw(&self); 3 | } 4 | 5 | pub struct Screen { 6 | pub components: Vec>, 7 | } 8 | 9 | impl Screen { 10 | pub fn run(&self) { 11 | for component in self.components.iter() { 12 | component.draw(); 13 | } 14 | } 15 | } 16 | 17 | pub struct Button { 18 | pub width: u32, 19 | pub height: u32, 20 | pub label: String, 21 | } 22 | 23 | impl Draw for Button { 24 | fn draw(&self) { 25 | // code to draw a button 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /playground/16-oop-features/src/main.rs: -------------------------------------------------------------------------------- 1 | use oop::Post; 2 | 3 | fn main() { 4 | let mut post = Post::new(); 5 | 6 | post.add_text("I ate a salad for lunch today"); 7 | // assert_eq!("", post.content()); 8 | 9 | let post = post.request_review(); 10 | // assert_eq!("", post.content()); 11 | 12 | let post = post.approve(); 13 | assert_eq!("I ate a salad for lunch today", post.content()); 14 | // println!("{}", post.content()); 15 | } 16 | -------------------------------------------------------------------------------- /playground/16-oop-features/src/main_traits.rs: -------------------------------------------------------------------------------- 1 | use oop::Draw; 2 | use oop::{Button, Screen}; 3 | 4 | struct SelectBox { 5 | width: u32, 6 | height: u32, 7 | options: Vec, 8 | } 9 | 10 | impl Draw for SelectBox { 11 | fn draw(&self) { 12 | // code to draw a select box 13 | } 14 | } 15 | 16 | fn main() { 17 | let screen = Screen { 18 | compoents: vec![ 19 | Box::new(SelectBox { 20 | width: 75, 21 | height: 10, 22 | options: vec![ 23 | String::from("Yes"), 24 | String::from("Maybe"), 25 | String::from("No"), 26 | ], 27 | }), 28 | Box::new(Button { 29 | width: 50, 30 | height: 10, 31 | label: String::from("OK"), 32 | }), 33 | ], 34 | }; 35 | screen.run(); 36 | } 37 | -------------------------------------------------------------------------------- /playground/17-patterns-matching/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "patterns" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /playground/17-patterns-matching/src/main.rs: -------------------------------------------------------------------------------- 1 | // mod patterns; 2 | // mod refutability; 3 | mod pattern_syntax; 4 | 5 | fn main() { 6 | // patterns::run(); 7 | // refutability::run(); 8 | pattern_syntax::run(); 9 | } 10 | -------------------------------------------------------------------------------- /playground/17-patterns-matching/src/pattern_syntax.rs: -------------------------------------------------------------------------------- 1 | pub fn run() { 2 | // literals 3 | // let x = 1; 4 | // match x { 5 | // 1 => println!("one"), // one 6 | // 2 => println!("two"), 7 | // 3 => println!("three"), 8 | // _ => println!("anything"), 9 | // } 10 | 11 | // named variables 12 | // let x = Some(5); 13 | // let y = 10; 14 | 15 | // match x { 16 | // Some(50) => println!("Got 50"), 17 | // // Some(y) => println!("Matched, y = {:?}", y), // Matched, y = 5 18 | // Some(n) if n == y => println!("Matched, n = {:?}", n), 19 | // _ => println!("Default case, x = {:?}", x), // Default case, x = Some(5) 20 | // } 21 | // println!("at the end: x = {:?}, y = {:?}", x, y); 22 | 23 | // multiple patterns 24 | // let x = 1; 25 | // match x { 26 | // 1 | 2 => println!("one or two"), // one or two 27 | // 3 => println!("three"), 28 | // _ => println!("anything"), 29 | // } 30 | 31 | // matching range 32 | // let x = 5; 33 | // match x { 34 | // 1..=5 => println!("one through five"), // one through five 35 | // _ => println!("anything"), 36 | // } 37 | 38 | // let x = 'c'; 39 | // match x { 40 | // 'a'..='j' => println!("early ASCII letter"), // early ASCII letter 41 | // 'k'..='z' => println!("late ASCII letter"), 42 | // _ => println!("anything"), 43 | // } 44 | 45 | // destructuring 46 | // structs 47 | // let p = Point { x: 0, y: 7 }; 48 | 49 | // // let Point { x, y } = p; 50 | // // assert_eq!(0, x); 51 | // // assert_eq!(7, y); 52 | 53 | // match p { 54 | // Point { x, y: 0 } => println!("On the x axis at {}", x), 55 | // Point { x: 0, y } => println!("On the y axis at {}", y), // On the y axis at 7 56 | // Point { x, y } => println!("On neither axis: {}, {}", x, y), 57 | // } 58 | 59 | // enums 60 | // let msg = Message::ChangeColor(0, 160, 255); 61 | // let msg = Message::ChangeColor(Color::Rgb(0, 160, 255)); 62 | // match msg { 63 | // Message::Quit => { 64 | // println!("The Quit variant has no data to destructure.") 65 | // } 66 | // Message::Move { x, y } => { 67 | // println!("Move in the x direction {} and in the y direction {}", x, y); 68 | // } 69 | // Message::Write(text) => println!("Text message: {}", text), 70 | // // Message::ChangeColor(r, g, b) => { 71 | // Message::ChangeColor(Color::Rgb(r, g, b)) => { 72 | // println!("Change the color to red {}, green {}, and blue {}", r, g, b) 73 | // // Change the color to red 0, green 160, and blue 255 74 | // } 75 | // Message::ChangeColor(Color::Hsv(h, s, v)) => println!( 76 | // "Change the color to hue {}, saturation {}, and value {}", 77 | // h, s, v 78 | // ), 79 | // } 80 | 81 | // structs and tuples 82 | // let ((feet, inches), Point { x, y }) = ((3, 10), Point { x: 3, y: -10 }); 83 | 84 | // ignoring values 85 | // foo(3, 4); 86 | 87 | // ignoring parts of a value 88 | // let mut setting_value = Some(5); 89 | // let new_setting_value = Some(10); 90 | 91 | // match (setting_value, new_setting_value) { 92 | // (Some(_), Some(_)) => { 93 | // println!("Can't overwrite an existing customized value"); // Can't overwrite an existing customized value 94 | // } 95 | // _ => { 96 | // setting_value = new_setting_value; 97 | // } 98 | // } 99 | 100 | // println!("setting is {:?}", setting_value); 101 | 102 | // let numbers = (2, 4, 8, 16, 32); 103 | 104 | // match numbers { 105 | // (first, _, third, _, fifth) => { 106 | // println!("Some numbers: {}, {}, {}", first, third, fifth) 107 | // } 108 | // } 109 | 110 | // ignoring an unused variable 111 | // let _x = 5; 112 | // let y = 10; 113 | 114 | // ignoring remaining parts of a value 115 | // struct Point { 116 | // x: i32, 117 | // y: i32, 118 | // z: i32, 119 | // } 120 | 121 | // let origin = Point { x: 0, y: 0, z: 0 }; 122 | 123 | // match origin { 124 | // Point { x, .. } => println!("x is {}", x), // x is 0 125 | // } 126 | 127 | // let numbers = (2, 4, 8, 16, 32); 128 | 129 | // match numbers { 130 | // (first, .., last) => { 131 | // println!("Some numbers: {}, {}", first, last); // Some numbers: 2, 32 132 | // } 133 | // } 134 | 135 | // match guards 136 | // let num = Some(4); 137 | // match num { 138 | // Some(x) if x < 5 => println!("less than five: {}", x), 139 | // Some(x) => println!("{}", x), 140 | // None => (), 141 | // } 142 | 143 | let x = 4; 144 | let y = false; 145 | 146 | match x { 147 | 4 | 5 | 6 if y => println!("yes"), 148 | _ => println!("no"), // no 149 | } 150 | 151 | // binding 152 | enum Message { 153 | Hello { id: i32 }, 154 | } 155 | 156 | let msg = Message::Hello { id: 5 }; 157 | 158 | match msg { 159 | Message::Hello { 160 | id: id_variable @ 3..=7, 161 | } => println!("Found an id in range: {}", id_variable), // Found an id in range: 5 162 | Message::Hello { id: 10..=12 } => { 163 | println!("Found an id in another range") 164 | } 165 | Message::Hello { id } => println!("Found some other id: {}", id), 166 | } 167 | } 168 | 169 | // struct Point { 170 | // x: i32, 171 | // y: i32, 172 | // } 173 | 174 | // enum Message { 175 | // Quit, 176 | // Move { x: i32, y: i32 }, 177 | // Write(String), 178 | // // ChangeColor(i32, i32, i32), 179 | // ChangeColor(Color), 180 | // } 181 | 182 | // enum Color { 183 | // Rgb(i32, i32, i32), 184 | // Hsv(i32, i32, i32), 185 | // } 186 | 187 | // fn foo(_: i32, y: i32) { 188 | // println!("This code only uses the y parameter: {}", y); // This code only uses the y parameter: 4 189 | // } 190 | -------------------------------------------------------------------------------- /playground/17-patterns-matching/src/patterns.rs: -------------------------------------------------------------------------------- 1 | pub fn run() { 2 | // if let 3 | let favorite_color: Option<&str> = None; 4 | let is_tuesday = false; 5 | let age: Result = "34".parse(); 6 | 7 | if let Some(color) = favorite_color { 8 | println!("Using your favorite color, {}, as the background", color); 9 | } else if is_tuesday { 10 | println!("Tuesday is green day!"); 11 | } else if let Ok(age) = age { 12 | if age > 30 { 13 | println!("Using purple as the background color"); // printed 14 | } else { 15 | println!("Using orange as the background color"); 16 | } 17 | } else { 18 | println!("Using blue as the background color"); 19 | } 20 | 21 | // while let 22 | let mut stack = Vec::new(); 23 | stack.push(1); 24 | stack.push(2); 25 | stack.push(3); 26 | 27 | while let Some(top) = stack.pop() { 28 | println!("{}", top); // 3 2 1 29 | } 30 | 31 | // for loops 32 | let v = vec!['a', 'b', 'c']; 33 | 34 | for (index, value) in v.iter().enumerate() { 35 | println!("{} is at index {}", value, index); 36 | // a is at index 0 37 | // b is at index 1 38 | // c is at index 2 39 | } 40 | 41 | // let statements 42 | // let PATTERN = EXPRESSION; 43 | // let (x, y, z) = (1, 2, 3); 44 | 45 | // function parameters 46 | let point = (3, 5); 47 | print_coordinates(&point); 48 | } 49 | 50 | fn print_coordinates(&(x, y): &(i32, i32)) { 51 | println!("Current location: ({}, {})", x, y) // Current location: (3, 5) 52 | } 53 | -------------------------------------------------------------------------------- /playground/17-patterns-matching/src/refutability.rs: -------------------------------------------------------------------------------- 1 | pub fn run() { 2 | // let Some(x) = some_option_value; // `let` bindings require an "irrefutable pattern", pattern `None` not covered 3 | // if let Some(x) = some_option_value { 4 | // println!("{}", x); 5 | // } 6 | // if let x = 5 { 7 | // // this pattern will always match, so the `if let` is useless 8 | // println!("{}", x); 9 | // }; 10 | } 11 | -------------------------------------------------------------------------------- /playground/18-advanced-features/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "advanced" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /playground/18-advanced-features/hello_macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello_macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /playground/18-advanced-features/hello_macro/hello_macro_derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | # procedural macros need to be in their own crate. 2 | 3 | [package] 4 | name = "hello_macro_derive" 5 | version = "0.1.0" 6 | edition = "2021" 7 | 8 | [lib] 9 | proc-macro = true 10 | 11 | [dependencies] 12 | syn = "1.0" 13 | quote = "1.0" 14 | -------------------------------------------------------------------------------- /playground/18-advanced-features/hello_macro/hello_macro_derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate proc_macro; // allows us to read and manipulate Rust code from our code 2 | 3 | use proc_macro::TokenStream; 4 | use quote::quote; // turns syn data structures back into Rust code. 5 | use syn; // parses Rust code from a string into a data structure that we can perform operations on 6 | 7 | #[proc_macro_derive(HelloMacro)] 8 | pub fn hello_macro_derive(input: TokenStream) -> TokenStream { 9 | // Construct a representation of Rust code as a syntax tree 10 | // that we can manipulate 11 | let ast = syn::parse(input).unwrap(); 12 | // Build the trait implementation 13 | impl_hello_macro(&ast) 14 | } 15 | 16 | fn impl_hello_macro(ast: &syn::DeriveInput) -> TokenStream { 17 | let name = &ast.ident; 18 | let gen = quote! { 19 | impl HelloMacro for #name { 20 | fn hello_macro() { 21 | println!("Hello, Macro! My name is {}!", stringify!(#name)); 22 | } 23 | } 24 | }; 25 | gen.into() 26 | } 27 | -------------------------------------------------------------------------------- /playground/18-advanced-features/hello_macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub trait HelloMacro { 2 | fn hello_macro(); 3 | } 4 | -------------------------------------------------------------------------------- /playground/18-advanced-features/hello_macro/src/main.rs: -------------------------------------------------------------------------------- 1 | use hello_macro::HelloMacro; 2 | // use hello_macro_derive::HelloMacro; 3 | 4 | struct Pancakes; 5 | 6 | impl HelloMacro for Pancakes { 7 | fn hello_macro() { 8 | println!("Hello, Macro! My name is Pancakes!"); 9 | } 10 | } 11 | 12 | fn main() { 13 | Pancakes::hello_macro(); 14 | } 15 | -------------------------------------------------------------------------------- /playground/18-advanced-features/pancakes/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pancakes" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | hello_macro = { path = "../hello_macro" } 10 | hello_macro_derive = { path = "../hello_macro/hello_macro_derive" } -------------------------------------------------------------------------------- /playground/18-advanced-features/pancakes/src/main.rs: -------------------------------------------------------------------------------- 1 | use hello_macro::HelloMacro; 2 | use hello_macro_derive::HelloMacro; 3 | 4 | #[derive(HelloMacro)] 5 | struct Pancakes; 6 | 7 | fn main() { 8 | Pancakes::hello_macro(); // Hello, Macro! My name is Pancakes! 9 | } 10 | -------------------------------------------------------------------------------- /playground/18-advanced-features/src/advanced_functions.rs: -------------------------------------------------------------------------------- 1 | pub fn run() { 2 | let answer = do_twice(add_one, 5); 3 | println!("The answer is: {}", answer); // 12 4 | 5 | let list_of_numbers = vec![1, 2, 3]; 6 | // let list_of_strings: Vec = list_of_numbers.iter().map(|i| i.to_string()).collect(); 7 | let list_of_strings: Vec = list_of_numbers.iter().map(ToString::to_string).collect(); 8 | let list_of_statuses: Vec = (0u32..20).map(Status::Value).collect(); 9 | } 10 | 11 | fn add_one(x: i32) -> i32 { 12 | x + 1 13 | } 14 | 15 | fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 { 16 | f(arg) + f(arg) 17 | } 18 | 19 | enum Status { 20 | Value(u32), 21 | Stop, 22 | } 23 | 24 | fn returns_closure() -> Box i32> { 25 | Box::new(|x| x + 1) 26 | } 27 | -------------------------------------------------------------------------------- /playground/18-advanced-features/src/advanced_traits.rs: -------------------------------------------------------------------------------- 1 | // Specifying Placeholder Types in Trait Definitions with Associated Types 2 | // pub trait Iterator { 3 | // type Item; 4 | 5 | // fn next(&mut self) -> Option; 6 | // } 7 | 8 | // struct Counter { 9 | // count: u32, 10 | // } 11 | 12 | // impl Iterator for Counter { 13 | // type Item = u32; 14 | 15 | // fn next(&mut self) -> Option {} 16 | // } 17 | 18 | // Default Generic Type Parameters and Operator Overloading 19 | // use std::ops::Add; 20 | 21 | // #[derive(Debug, Copy, Clone, PartialEq)] 22 | // struct Point { 23 | // x: i32, 24 | // y: i32, 25 | // } 26 | 27 | // impl Add for Point { 28 | // type Output = Point; 29 | 30 | // fn add(self, other: Point) -> Point { 31 | // Point { 32 | // x: self.x + other.x, 33 | // y: self.y + other.y, 34 | // } 35 | // } 36 | // } 37 | 38 | // trait Add { 39 | // type Output; 40 | 41 | // fn add(self, rhs: Rhs) -> Self::Output; 42 | // // The Rhs generic type parameter (short for “right hand side”) defines the type of the rhs parameter in the add method. 43 | // } 44 | 45 | // struct Millimeters(u32); 46 | // struct Meters(u32); 47 | 48 | // impl Add for Millimeters { 49 | // type Output = Millimeters; 50 | 51 | // fn add(self, other: Meters) -> Millimeters { 52 | // Millimeters(self.0 + (other.0 * 1000)) 53 | // } 54 | // } 55 | 56 | // pub fn run() { 57 | // assert_eq!( 58 | // Point { x: 1, y: 0 } + Point { x: 2, y: 3 }, 59 | // Point { x: 3, y: 3 } 60 | // ); 61 | // } 62 | 63 | // Fully Qualified Syntax for Disambiguation: Calling Methods with the Same Name 64 | // trait Pilot { 65 | // fn fly(&self); 66 | // } 67 | 68 | // trait Wizard { 69 | // fn fly(&self); 70 | // } 71 | 72 | // struct Human; 73 | 74 | // impl Pilot for Human { 75 | // fn fly(&self) { 76 | // println!("This is your captain speaking."); 77 | // } 78 | // } 79 | 80 | // impl Wizard for Human { 81 | // fn fly(&self) { 82 | // println!("Up!"); 83 | // } 84 | // } 85 | 86 | // impl Human { 87 | // fn fly(&self) { 88 | // println!("*waving arms furiously*"); 89 | // } 90 | // } 91 | 92 | // pub fn run() { 93 | // let person = Human; 94 | // Pilot::fly(&person); // This is your captain speaking. 95 | // Wizard::fly(&person); // Up! 96 | // person.fly(); // *waving arms furiously* 97 | // } 98 | 99 | // trait Animal { 100 | // fn baby_name() -> String; 101 | // } 102 | 103 | // struct Dog; 104 | 105 | // impl Dog { 106 | // fn baby_name() -> String { 107 | // String::from("Spot") 108 | // } 109 | // } 110 | 111 | // impl Animal for Dog { 112 | // fn baby_name() -> String { 113 | // String::from("puppy") 114 | // } 115 | // } 116 | 117 | // pub fn run() { 118 | // // println!("A baby dog is called a {}", Dog::baby_name()); // A baby dog is called a Spot 119 | // // println!("A baby dog is called a {}", Animal::baby_name()); // Rust can’t figure out which implementation of Animal::baby_name we want 120 | // println!("A baby dog is called a {}", ::baby_name()); // A baby dog is called a puppy 121 | // // ::function(receiver_if_method, next_arg, ...); 122 | // } 123 | 124 | // Using Supertraits to Require One Trait’s Functionality Within Another Trait 125 | // use std::fmt; 126 | 127 | // trait OutlinePrint: fmt::Display { 128 | // fn outline_print(&self) { 129 | // let output = self.to_string(); 130 | // let len = output.len(); 131 | // println!("{}", "*".repeat(len + 4)); 132 | // println!("*{}*", " ".repeat(len + 2)); 133 | // println!("* {} *", output); 134 | // println!("*{}*", " ".repeat(len + 2)); 135 | // println!("{}", "*".repeat(len + 4)); 136 | // } 137 | // } 138 | 139 | // struct Point { 140 | // x: i32, 141 | // y: i32, 142 | // } 143 | 144 | // // impl OutlinePrint for Point {} 145 | // impl fmt::Display for Point { 146 | // fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 147 | // write!(f, "({}, {})", self.x, self.y) 148 | // } 149 | // } 150 | 151 | // Using the Newtype Pattern to Implement External Traits on External Types 152 | // use std::fmt; 153 | 154 | // struct Wrapper(Vec); 155 | 156 | // impl fmt::Display for Wrapper { 157 | // fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 158 | // write!(f, "[{}]", self.0.join(", ")) 159 | // } 160 | // } 161 | 162 | // pub fn run() { 163 | // let w = Wrapper(vec![String::from("hello"), String::from("world")]); 164 | // println!("w = {}", w); 165 | // } 166 | -------------------------------------------------------------------------------- /playground/18-advanced-features/src/advanced_types.rs: -------------------------------------------------------------------------------- 1 | pub fn run() { 2 | // Creating Type Synonyms with Type Aliases 3 | // type Kilometers = i32; 4 | // let x: i32 = 5; 5 | // let y: Kilometers = 5; 6 | // println!("x + y = {}", x + y); // 10 7 | 8 | // let f: Thunk = Box::new(|| println!("hi")); 9 | } 10 | 11 | // type Thunk = Box; 12 | 13 | // fn takes_long_type(f: Thunk) { 14 | // // --snip-- 15 | // } 16 | 17 | // The Never Type that Never Returns 18 | // fn bar() -> ! { 19 | // print!("forever "); 20 | // loop { 21 | // print!("and ever "); 22 | // } 23 | // } 24 | -------------------------------------------------------------------------------- /playground/18-advanced-features/src/main.rs: -------------------------------------------------------------------------------- 1 | // mod unsafe_rust; 2 | // mod advanced_traits; 3 | // mod advanced_types; 4 | mod advanced_functions; 5 | 6 | fn main() { 7 | // unsafe_rust::run(); 8 | // advanced_traits::run(); 9 | // advanced_types::run(); 10 | advanced_functions::run(); 11 | } 12 | -------------------------------------------------------------------------------- /playground/18-advanced-features/src/unsafe_rust.rs: -------------------------------------------------------------------------------- 1 | // use std::slice; 2 | 3 | pub fn run() { 4 | // Dereferencing a Raw Pointer 5 | // let mut num = 5; 6 | // let r1 = &num as *const i32; 7 | // let r2 = &mut num as *mut i32; 8 | // unsafe { 9 | // println!("r1 is {}", *r1); // r1 is 5 10 | // println!("r2 is {}", *r2); // r2 is 5 11 | // } 12 | 13 | // Calling an Unsafe Function or Method 14 | // unsafe { 15 | // dangerous(); 16 | // } 17 | 18 | // let mut v = vec![1, 2, 3, 4, 5, 6]; 19 | // let r = &mut v[..]; 20 | // let (a, b) = r.split_at_mut(3); 21 | // assert_eq!(a, &mut [1, 2, 3]); 22 | // assert_eq!(b, &mut [4, 5, 6]); 23 | 24 | // Using extern Functions to Call External Code 25 | // unsafe { 26 | // println!("Absolute value of -3 according to C: {}", abs(-3)); 27 | // } 28 | 29 | // Accessing or Modifying a Mutable Static Variable 30 | println!("name is: {}", HELLO_WORLD); 31 | add_to_count(3); 32 | unsafe { 33 | println!("COUNTER: {}", COUNTER); // 3 34 | } 35 | } 36 | 37 | // unsafe fn dangerous() {} 38 | 39 | // fn split_at_mut(slice: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) { 40 | // let len = slice.len(); 41 | // let ptr = slice.as_mut_ptr(); 42 | // assert!(mid <= len); 43 | 44 | // // (&mut slice[..mid], &mut slice[mid..]) // cannot borrow `*slice` as mutable more than once at a time 45 | // // Rust’s borrow checker can’t understand that we’re borrowing different parts of the slice 46 | // unsafe { 47 | // ( 48 | // slice::from_raw_parts_mut(ptr, mid), 49 | // slice::from_raw_parts_mut(ptr.add(mid), len - mid), 50 | // ) 51 | // } 52 | // } 53 | 54 | // extern "C" { 55 | // fn abs(input: i32) -> i32; 56 | // } 57 | 58 | // #[no_mangle] 59 | // pub extern "C" fn call_from_c() { 60 | // println!("Just called a Rust function from C!"); 61 | // } 62 | 63 | static HELLO_WORLD: &str = "Hello, world"; 64 | static mut COUNTER: u32 = 0; 65 | 66 | fn add_to_count(inc: u32) { 67 | unsafe { 68 | COUNTER += inc; 69 | } 70 | } 71 | 72 | // Implementing an Unsafe Trait 73 | unsafe trait Foo { 74 | // methods go here 75 | } 76 | 77 | unsafe impl Foo for i32 { 78 | // method implementations go here 79 | } 80 | 81 | // Accessing Fields of a Union 82 | // A union is similar to a struct, but only one declared field is used in a particular instance at one time. 83 | --------------------------------------------------------------------------------