├── .editorconfig ├── .gitignore ├── .vscode ├── extensions.json └── settings.json ├── HowToLearnRust.md ├── LICENSE ├── README.md ├── examples └── hello │ ├── Cargo.toml │ └── src │ ├── lib.rs │ ├── lib │ └── russian.rs │ ├── main.rs │ ├── russian.rs │ ├── russian │ └── authors.rs │ └── spanish.rs └── exercise ├── a_variables ├── ANSWERS.md └── README.md ├── b_functions ├── Cargo.toml ├── README.md └── src │ └── main.rs ├── c_simple_types ├── Cargo.toml ├── README.md └── src │ └── main.rs ├── d_control_flow_strings ├── Cargo.toml ├── README.md └── src │ └── main.rs ├── e_ownership_references ├── Cargo.toml ├── README.md └── src │ └── main.rs ├── f_structs_traits ├── Cargo.toml ├── README.md └── src │ └── main.rs ├── g_collections_enums ├── Cargo.toml ├── README.md └── src │ └── main.rs ├── h_closures_threads ├── Cargo.toml └── src │ └── main.rs └── z_final_project ├── Cargo.toml ├── README.md ├── dyson.png ├── pens.png └── src └── main.rs /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | indent_style = space 4 | indent_size = 4 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | **/target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | **/Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | # Generated by VS Code 13 | **/.vscode 14 | 15 | # Generated by IntelliJ 16 | **/.idea 17 | # Files from macOS 18 | .DS_Store 19 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "rust-lang.rust-analyzer", 4 | "tamasfe.even-better-toml" 5 | ] 6 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.linkedProjects": [ 3 | "examples/hello/Cargo.toml", 4 | "exercise/b_functions/Cargo.toml", 5 | "exercise/c_simple_types/Cargo.toml", 6 | "exercise/d_control_flow_strings/Cargo.toml", 7 | "exercise/e_ownership_references/Cargo.toml", 8 | "exercise/f_structs_traits/Cargo.toml", 9 | "exercise/g_collections_enums/Cargo.toml", 10 | "exercise/h_closures_threads/Cargo.toml", 11 | "exercise/z_final_project/Cargo.toml", 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /HowToLearnRust.md: -------------------------------------------------------------------------------- 1 | # How To Learn Rust 2 | 3 | Perhaps more important than a crash course tutorial in Rust is **learning how to learn Rust**. Learning how to learn 4 | Rust will put you on a path that will lead to mastering the subject. 5 | 6 | ### IDE / Editor 7 | 8 | Though not always as light and nimble as a dedicated editor, an IDE can be really helpful in learning Rust. IDE support is part of the core Rust project, and it works well. Much more than just syntax highlighting, an IDE like [VS Code] or [IntelliJ] will integrate with the compiler and offer type hints, display errors, link to documentation, offer code completion, and much more. 9 | 10 | - Google the terms: `rust (name of the IDE or Editor you use)` 11 | - Need a suggestion? [Visual Studio Code] and [IntelliJ] are both great choices (and there are many more...) 12 | - Find the correct way to install Rust support for your IDE or Editor (it's often a plugin) 13 | - Install TOML support, which is usually separate from Rust support ([TOML](https://github.com/toml-lang/toml) 14 | is the config file format that Rust uses) 15 | - ...wait for it... 16 | - Be amazed at all the helpful auto-complete, etc. that turns on. Yay! 17 | - Customize your editor to your liking. 18 | 19 | [VS Code]: https://code.visualstudio.com/ 20 | [Visual Studio Code]: https://code.visualstudio.com/ 21 | [IntelliJ]: https://www.jetbrains.com/idea/ 22 | 23 | 24 | ### Find Answers 25 | 26 | You are always going to have questions. Here is how you find the answers. 27 | 28 | - If it is about something the standard library, then Google: `rust std (thing you want to find)` 29 | - For example, can't quite remember what that method on `Vec` was? Google `rust std Vec` 30 | - There is a very welcoming [Rust Community](https://www.rust-lang.org/community) out there that you can 31 | communicate with. See the link above for: 32 | - Forums 33 | - IRC channels 34 | - StackOverflow topics 35 | - News (The [weekly newsletter](https://this-week-in-rust.org/) is seriously fantastic), and I'm also quite partial 36 | to [Rust GameDev news](https://rust-gamedev.github.io/) 37 | - YouTube channel 38 | - User Groups and Meetups 39 | - Where to find and communicate with all the core Rust Teams 40 | 41 | ### Play Around 42 | 43 | Code something. Don't just sit and watch the course. Try stuff out! 44 | 45 | - Do the [exercises](https://github.com/CleanCut/ultimate_rust_crash_course#exercises)! 46 | - Don't be afraid to just `cargo new blah` and write a 5-line throwaway program to try something out. 47 | - Start an interesting little project 48 | - If you get stuck, or the project gets boring...no worries! Just start another interesting little project... 49 | - Find an existing project that looks interesting 50 | - Try it out 51 | - Try to contribute a bug fix or feature 52 | - Rewrite some existing little project in Rust (in a new project) 53 | - Compare the results 54 | - What did you like better about Rust? 55 | - What did you like better about the other language? 56 | - Compare binary size, memory usage, speed, etc. 57 | - Write a blog post about your experience! 58 | 59 | 60 | ### Tools 61 | 62 | There are tools that help you learn as well. 63 | 64 | - [Clippy](https://github.com/rust-lang/rust-clippy) is a super-amazing linter. It will tell you how to change 65 | working code into _idiomatic_ and _high-performing_ code. 66 | - [rustfmt](https://github.com/rust-lang/rustfmt) will format your code according to Rust style guidelines. 67 | There's only one set of Rust style guidelines...so there's nothing to argue about! Unfortunately, the project is 68 | right in the middle of a major overhaul...so it pretty much only works if you're using the nightly compiler (sigh). 69 | 70 | ### Reading 71 | 72 | Long-format reading is really interesting and informative. You will learn some things plowing through a comprehensive 73 | book that you would never have encountered during years of reading random bits of the standard library reference. I 74 | found these books _especially_ useful and high quality: 75 | 76 | **Books** 77 | 78 | - [Programming Rust, 2nd Edition](https://amzn.to/3i0NySP) - The (second edition of the) O'Reilly book by Jim Blandy, Jason Orendorff, and Leanora Tindall. Fantastic book 79 | focused on using the Rust language. This is the book _I_ used to learn Rust. 80 | - [The Rust Programming Language](https://doc.rust-lang.org/book/), aka "The Book" - the official free online book 81 | about the language, though you can [purchase a physical copy](https://amzn.to/2Vq0giK) if you prefer. 82 | - [Rust for Rustaceans](https://amzn.to/3Iavf8b) - A short, but incredibly action-packed book diving into some advanced topics. I loved this book. Read it after you have a solid grasp of Rust and want to go deeper. 83 | - [The Rustnomicon](https://doc.rust-lang.org/nomicon/) - The ultimate (unfinished, evolving) book about the deepest mysteries of Rust. Strap in! 84 | 85 | **Informational** 86 | 87 | - [Entering the Quantum Era—How Firefox got fast again and where it’s going to get faster](https://hacks.mozilla.org/2017/11/entering-the-quantum-era-how-firefox-got-fast-again-and-where-its-going-to-get-faster/) 88 | 89 | **Things we mentioned but didn't cover in depth** 90 | - [TOML Format](https://github.com/toml-lang/toml) - the config file format Rust uses 91 | - [Semantic Versioning](https://semver.org/) and [Cargo's Version Field Rules](https://doc.rust-lang.org/cargo/reference/manifest.html#the-version-field) 92 | - [The Edition Guide](https://doc.rust-lang.org/nightly/edition-guide/introduction.html) - Differences between Rust 2015 and Rust 2018 93 | - [String Formatting](https://doc.rust-lang.org/std/fmt/index.html) - `print!()`, `println!()`, `format!()`, etc. and 94 | how to deal with the format string. 95 | - [Firefox has over 3 million lines of Rust Code](https://www.openhub.net/p/firefox/analyses/latest/languages_summary) 96 | 97 | **More information about things we learned** 98 | - [Cargo](https://doc.rust-lang.org/book/ch01-03-hello-cargo.html) and 99 | [dependencies](https://doc.rust-lang.org/book/ch02-00-guessing-game-tutorial.html#using-a-crate-to-get-more-functionality) 100 | - [Variables, Mutability, and Shadowing](https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html) 101 | - [Functions](https://doc.rust-lang.org/book/ch03-03-how-functions-work.html) - fn 102 | - [Modules](https://doc.rust-lang.org/book/ch07-02-defining-modules-to-control-scope-and-privacy.html) 103 | and [pub](https://doc.rust-lang.org/book/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html#exposing-paths-with-the-pub-keyword) 104 | and [use](https://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-with-the-use-keyword.html) 105 | - [Scalar Types](https://doc.rust-lang.org/book/ch03-02-data-types.html#scalar-types) - 106 | Integers, Floating-point, Boolean, Characters. 107 | - [Compound Types](https://doc.rust-lang.org/book/ch03-02-data-types.html#compound-types) - 108 | Tuples, Arrays. 109 | - [Control Flow](https://doc.rust-lang.org/book/ch03-05-control-flow.html) - if, while, for 110 | - [Threads](https://doc.rust-lang.org/book/ch16-01-threads.html) 111 | and [closures](https://doc.rust-lang.org/book/ch13-01-closures.html) 112 | - [Ownership and Scope](https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html) 113 | - [References & Borrowing](https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html) 114 | - Common Collections: [Vectors](https://doc.rust-lang.org/book/ch08-01-vectors.html), 115 | [Strings](https://doc.rust-lang.org/book/ch08-02-strings.html), 116 | and [Hash Maps](https://doc.rust-lang.org/book/ch08-03-hash-maps.html) 117 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Nathan Stocks 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ultimate Rust Crash Course 2 | 3 | This is the companion repository for the [Ultimate Rust Crash Course] published online, presented 4 | live at O'Reilly virtual events, or in person. You will get the most out of this training 5 | experience by trying to accomplish the [exercises] in this repository and watching (or attending) 6 | the instructor-led training. 7 | 8 | In other words, this repository is for you hands-on-learners! 9 | 10 | I use macOS, and that is what I developed this course on. Everything _ought_ to work similarly on 11 | major Linux distributions and Windows. Please [contact me](mailto:nathan@agileperception.com) ASAP 12 | if you have trouble with anything on this page. 13 | 14 | _Did you like this course? Check out the next one: [Ultimate Rust 2: Intermediate Concepts]_ 15 | 16 | ## Install Rust 17 | 18 | Rust is required for this course! The latest stable version is always recommended. 19 | 20 | - Go to [rust-lang.org](https://rust-lang.org) and click on the `Get Started` 21 | button and follow the instructions to install Rust for your operating system. 22 | - Please DO NOT install rust via some other package manager. It will probably be a version that is _really old_. 23 | 24 | You should get somewhat similar output if you run commands like the ones below (newer versions are okay). If you 25 | already have an old version of Rust installed, then run `rustup update` to install a newer version. 26 | 27 | ```shell 28 | $ rustc --version 29 | rustc 1.54.0 (a178d0322 2021-07-26) 30 | $ cargo --version 31 | cargo 1.54.0 (5ae8d74b3 2021-06-22) 32 | ``` 33 | 34 | - Clone or download this repository to your computer. 35 | 36 | ## Prepare Your Development Environment 37 | 38 | Please do the following (see the [How To Learn Rust](https://github.com/CleanCut/ultimate_rust_crash_course/blob/master/HowToLearnRust.md) 39 | page for details on all of these) 40 | - [ ] Choose an IDE (or Editor) and configure it with Rust support and customize it to your liking 41 | - **VS Code users**: Please use the [`rust-analyzer`] extension. **_If you have the `rust` extension installed, please uninstall it!_** 42 | - **IntelliJ users**: Please use the [`intellij-rust`] extension. 43 | - [ ] Choose one place to "find answers" and either introduce yourself (if it's a forum, IRC, etc.) or find the answer 44 | to one question you have. 45 | - [ ] Try doing something in Rust! If you don't have a better idea, then just do this: 46 | - `cargo new message` 47 | - `cd message` 48 | - `cargo run` 49 | - Edit `src/main.rs` and change the message. 50 | - `cargo run` again to see your new message. 51 | - [ ] Check out the descriptions of the tools and books. 52 | 53 | # Training! 54 | 55 | Now you are ready for the training! Go watch the [Ultimate Rust Crash Course] (or attend the live 56 | session) and come back here for the [exercises]. 57 | 58 | # Resources 59 | 60 | - Training by the instructor (Nathan Stocks) in the form of the [Ultimate Rust Crash Course] or a 61 | live session. 62 | - This Repository :tada: 63 | - [How To Learn Rust](https://github.com/CleanCut/ultimate_rust_crash_course/blob/master/HowToLearnRust.md) 64 | - [The Rust Standard Library](https://doc.rust-lang.org/std/) 65 | 66 | # Exercises 67 | 68 | Please clone this repository! These exercises are designed as Rust projects for you to edit on your 69 | own computer, with the exception of Exercise A (which is just a `README.md` file). 70 | 71 | The exercises are separate Rust projects inside the `exercises/` subdirectory. For each exercise, 72 | you should: 73 | - Open the corresponding`exercise/EXERCISE_NAME` directory in your IDE/Editor 74 | - Seriously, just open the _individual exercise directory_ in your IDE. If you open the entire repository, your IDE will probably complain that it sees multiple Rust projects. 75 | - Navigate to the same directory with your Terminal application (so you can run `cargo run`, etc.) 76 | - Open up the `src/main.rs` file. 77 | - Follow the numbered exercise instructions in the code comments. 78 | 79 | If you encounter any problems with the exercises, please feel free to use the online course 80 | communication tools to contact me, or [open an discussion]. Either way. 😄 81 | 82 | For your convenience, here is a list of all the exercises, with links to view the code on GitHub. 83 | 84 | - [Exercise A - Variables & Scope](https://github.com/CleanCut/ultimate_rust_crash_course/tree/master/exercise/a_variables) 85 | - [Exercise B - Functions](https://github.com/CleanCut/ultimate_rust_crash_course/tree/master/exercise/b_functions) 86 | - [Exercise C - Simple Types](https://github.com/CleanCut/ultimate_rust_crash_course/tree/master/exercise/c_simple_types) 87 | - [Exercise D - Control Flow & Strings](https://github.com/CleanCut/ultimate_rust_crash_course/tree/master/exercise/d_control_flow_strings) 88 | - [Exercise E - Ownership & References](https://github.com/CleanCut/ultimate_rust_crash_course/tree/master/exercise/e_ownership_references) 89 | - [Exercise F - Structs & Traits](https://github.com/CleanCut/ultimate_rust_crash_course/tree/master/exercise/f_structs_traits) 90 | - [Exercise G - Collections & Enums](https://github.com/CleanCut/ultimate_rust_crash_course/tree/master/exercise/g_collections_enums) 91 | - [Exercise H - Closures & Threads](https://github.com/CleanCut/ultimate_rust_crash_course/tree/master/exercise/h_closures_threads) 92 | - [Exercise Z - Final Project](https://github.com/CleanCut/ultimate_rust_crash_course/tree/master/exercise/z_final_project) 93 | 94 | # Projects 95 | 96 | - [Invaders](https://github.com/CleanCut/invaders) - A terminal-based Space Invaders arcade game clone. 97 | 98 | 99 | [exercises]: https://github.com/CleanCut/ultimate_rust_crash_course#exercises 100 | [open an discussion]: https://github.com/CleanCut/ultimate_rust_crash_course/discussions/new 101 | [Ultimate Rust Crash Course]: https://agileperception.com/ultimate_rust_crash_course 102 | [Ultimate Rust 2: Intermediate Concepts]: https://github.com/CleanCut/ultimate_rust2 103 | [`rust-analyzer`]: https://marketplace.visualstudio.com/items?itemName=matklad.rust-analyzer 104 | [`intellij-rust`]: https://intellij-rust.github.io/ 105 | -------------------------------------------------------------------------------- /examples/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 | rand = "0.8" 10 | -------------------------------------------------------------------------------- /examples/hello/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod russian; 2 | pub mod spanish; 3 | 4 | pub mod english { 5 | pub fn greet() { 6 | println!("Hi!"); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/hello/src/lib/russian.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CleanCut/ultimate_rust_crash_course/6f923ed7b9c2a9ea6e56a7335a67c59fbe926587/examples/hello/src/lib/russian.rs -------------------------------------------------------------------------------- /examples/hello/src/main.rs: -------------------------------------------------------------------------------- 1 | use hello::{english, russian, spanish}; 2 | 3 | fn main() { 4 | english::greet(); 5 | spanish::greet(); 6 | russian::greet(); 7 | } 8 | -------------------------------------------------------------------------------- /examples/hello/src/russian.rs: -------------------------------------------------------------------------------- 1 | pub mod authors; 2 | 3 | pub fn greet() { 4 | println!("Привет!"); 5 | } 6 | -------------------------------------------------------------------------------- /examples/hello/src/russian/authors.rs: -------------------------------------------------------------------------------- 1 | pub const DOSTOEVSKY: &'static str = "Достоевский"; 2 | -------------------------------------------------------------------------------- /examples/hello/src/spanish.rs: -------------------------------------------------------------------------------- 1 | pub fn greet() { 2 | println!("¡Hola!"); 3 | } 4 | -------------------------------------------------------------------------------- /exercise/a_variables/ANSWERS.md: -------------------------------------------------------------------------------- 1 | # Answers to Exercise A 2 | 3 | ### Part 1 4 | 5 | ```shell 6 | $ cargo new variables 7 | ``` 8 | 9 | ```toml 10 | # Cargo.toml 11 | 12 | [package] 13 | name = "variables" 14 | version = "2.3.4" 15 | # ... 16 | ``` 17 | 18 | 19 | ```rust 20 | // src/main.rs 21 | 22 | fn main() { 23 | let missiles = 8; 24 | let ready = 2; 25 | println!("Firing {} of my {} missiles...", ready, missiles); 26 | } 27 | 28 | ``` 29 | 30 | ```shell 31 | $ cargo run 32 | ``` 33 | 34 | ### Part 2 35 | 36 | ```rust 37 | // src/main.rs 38 | 39 | fn main() { 40 | let missiles = 8; // Fix error by doing: let mut missiles = 8 41 | let ready = 2; 42 | println!("Firing {} of my {} missiles...", ready, missiles); 43 | missiles = missiles - ready; // Error! 44 | println!("{} missiles left", missiles); 45 | } 46 | ``` 47 | 48 | 49 | ```rust 50 | const STARTING_MISSILES: i32 = 8; 51 | const READY_AMOUNT: i32 = 2; 52 | 53 | fn main() { 54 | let mut missiles = STARTING_MISSILES; 55 | let ready = READY_AMOUNT; 56 | println!("Firing {} of my {} missiles...", ready, missiles); 57 | missiles = missiles - ready; 58 | println!("{} missiles left", missiles); 59 | } 60 | ``` 61 | 62 | ### Extra challenges 63 | 64 | - Explicitly annotate the variables with the type `i32` 65 | 66 | ```rust 67 | const STARTING_MISSILES: i32 = 8; 68 | const READY_AMOUNT: i32 = 2; 69 | 70 | fn main() { 71 | let mut missiles: i32 = STARTING_MISSILES; 72 | let ready: i32 = READY_AMOUNT; 73 | println!("Firing {} of my {} missiles...", ready, missiles); 74 | missiles = missiles - ready; 75 | println!("{} missiles left", missiles); 76 | } 77 | ``` 78 | 79 | - Try binding the variables all at once on one line using a pattern (parentheses and commas) -- can you figure out where `mut` goes? 80 | 81 | ```rust 82 | const STARTING_MISSILES: i32 = 8; 83 | const READY_AMOUNT: i32 = 2; 84 | 85 | fn main() { 86 | let (mut missiles, ready) = (STARTING_MISSILES, READY_AMOUNT); 87 | println!("Firing {} of my {} missiles...", ready, missiles); 88 | missiles = missiles - ready; 89 | println!("{} missiles left", missiles); 90 | } 91 | 92 | ``` 93 | 94 | - Can you figure out the correct type annotation when you assign them all in one line? Hint: it will use the same sort of pattern as the variables and values. 95 | 96 | ```rust 97 | const STARTING_MISSILES: i32 = 8; 98 | const READY_AMOUNT: i32 = 2; 99 | 100 | fn main() { 101 | let (mut missiles, ready): (i32, i32) = (STARTING_MISSILES, READY_AMOUNT); 102 | println!("Firing {} of my {} missiles...", ready, missiles); 103 | missiles = missiles - ready; 104 | println!("{} missiles left", missiles); 105 | } 106 | ``` 107 | 108 | - Instead of changing missiles, use the value `missiles - ready` directly in the second `println!(...)` 109 | - What does cargo say when you run your program? 110 | 111 | It gives this warning: 112 | 113 | ``` 114 | warning: variable does not need to be mutable 115 | ``` 116 | 117 | - Add another variable to your program *but don't use it*. 118 | - What does cargo say when you run your program? 119 | 120 | It gives this warning if my unused variable is named `jet`: 121 | 122 | ``` 123 | warning: unused variable: `jet` 124 | ``` 125 | 126 | - Try modifying a constant in main() (for example, `READY_AMOUNT = 1`). What does the error look like? 127 | 128 | ``` 129 | error[E0070]: invalid left-hand side of assignment 130 | --> src/main.rs:5:18 131 | | 132 | 5 | READY_AMOUNT = 1; 133 | | ------------ ^ 134 | | | 135 | | cannot assign to this expression 136 | ``` 137 | -------------------------------------------------------------------------------- /exercise/a_variables/README.md: -------------------------------------------------------------------------------- 1 | # Exercise A: Variables 2 | 3 | ### Part 1 4 | - [ ] Make a new project named `variables` using cargo 5 | - See "cargo help" if you forgot the command. 6 | - [ ] Open `Cargo.toml` 7 | - [ ] Change the version number to `2.3.4` and save the file. Keep an eye out for that version number in cargo's output when you run it! 8 | - [ ] In `src/main.rs` at the start of the `main()` function: 9 | - [ ] Declare the variable `missiles` and initialize it to `8` 10 | - [ ] Declare the variable `ready` and initialize it to `2` 11 | - [ ] Change the `println!(...)` at the end of `main()` to: 12 | - `println!("Firing {} of my {} missiles...", ready, missiles);` 13 | - [ ] Run your program using cargo (see "cargo help" if you forgot the command). 14 | Some common errors you may hit: 15 | - Forgot to use `let` to bind a variable 16 | - Forgot a semicolon `;` at the end of a line 17 | 18 | ### Part 2 19 | 20 | - [ ] After the `println!(...)`, subtract `ready` from `missiles` like this: 21 | - `missiles = missiles - ready;` 22 | - [ ] Add a second `println!(...)` to the end: 23 | - `println!("{} missiles left", missiles);` 24 | - [ ] Run your program again using cargo 25 | - Did you run into an error about mutability? Go back and add `mut` at the right spot on the line where you declare `missiles`. 26 | - [ ] Declare a constant named `STARTING_MISSILES` and set it to `8` (the type is `i32`). 27 | - [ ] Declare a constant named `READY_AMOUNT` and set it to `2` (also `i32`). 28 | - [ ] Use the constants to initialize `missiles` and `ready` 29 | - Where did you put the constants? If you put them inside the `main()` function, try moving them up above `main()` to module scope! 30 | - [ ] Nice. Congratulate yourself on a job well done! You are a Rust programmer now! 31 | 32 | ### Extra challenges: 33 | - [ ] Explicitly annotate the variables with the type `i32` 34 | - [ ] Try binding the variables all at once on one line using a pattern (parentheses and commas) -- can you figure out where `mut` goes? 35 | - [ ] Can you figure out the correct type annotation when you assign them all in one line? Hint: it will use the same sort of pattern as the variables and values. 36 | - [ ] Instead of changing missiles, use the value `missiles - ready` directly in the second `println!(...)` 37 | - What warning does cargo emit when you run your program now? Can you fix it? 38 | - [ ] Add another variable to your program *but don't use it*. 39 | - What does cargo say when you run your program? 40 | - [ ] Try modifying a constant in `main()` (for example, `READY_AMOUNT = 1`). What does the error look like? 41 | -------------------------------------------------------------------------------- /exercise/b_functions/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "b_functions" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /exercise/b_functions/README.md: -------------------------------------------------------------------------------- 1 | Please clone this repository, change to this directory, and open `src/main.rs` 2 | and follow the instructions in the comments. 3 | -------------------------------------------------------------------------------- /exercise/b_functions/src/main.rs: -------------------------------------------------------------------------------- 1 | // Silence some warnings so they don't distract from the exercise. 2 | #![allow(unused_variables)] 3 | 4 | fn main() { 5 | let width = 4; 6 | let height = 7; 7 | let depth = 10; 8 | // 1. Try running this code with `cargo run` and take a look at the error. 9 | // 10 | // See if you can fix the error. It is right around here, somewhere. If you succeed, then 11 | // doing `cargo run` should succeed and print something out. 12 | { 13 | let area = area_of(width, height); 14 | } 15 | println!("Area is {}", area); 16 | 17 | // 2. The area that was calculated is not correct! Go fix the area_of() function below, then run 18 | // the code again and make sure it worked (you should get an area of 28). 19 | 20 | // 3. Uncomment the line below. It doesn't work yet because the `volume` function doesn't exist. 21 | // Create the `volume` function! It should: 22 | // - Take three arguments of type i32 23 | // - Multiply the three arguments together 24 | // - Return the result (which should be 280 when you run the program). 25 | // 26 | // If you get stuck, remember that this is *very* similar to what `area_of` does. 27 | // 28 | //println!("Volume is {}", volume(width, height, depth)); 29 | } 30 | 31 | fn area_of(x: i32, y: i32) -> i32 { 32 | // 2a. Fix this function to correctly compute the area of a rectangle given 33 | // dimensions x and y by multiplying x and y and returning the result. 34 | // 35 | return 0; 36 | // Challenge: It isn't idiomatic (the normal way a Rust programmer would do things) to use 37 | // `return` on the last line of a function. Change the last line to be a 38 | // "tail expression" that returns a value without using `return`. 39 | // Hint: `cargo clippy` will warn you about this exact thing. 40 | } 41 | -------------------------------------------------------------------------------- /exercise/c_simple_types/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ding_machine" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /exercise/c_simple_types/README.md: -------------------------------------------------------------------------------- 1 | Please clone this repository, change to this directory, and open `src/main.rs` 2 | and follow the instructions in the comments. 3 | -------------------------------------------------------------------------------- /exercise/c_simple_types/src/main.rs: -------------------------------------------------------------------------------- 1 | // Silence some warnings so they don't distract from the exercise. 2 | #![allow(dead_code, unused_variables)] 3 | 4 | fn main() { 5 | let coords: (f32, f32) = (6.3, 15.0); 6 | // 1. Pass parts of `coords` to the `print_difference` function. This should show the difference 7 | // between the two numbers in coords when you do `cargo run`. Use tuple indexing. 8 | // 9 | // The `print_difference` function is defined below the `main` function. It may help if you look 10 | // at how it is defined. 11 | // 12 | //print_difference( ... ); // Uncomment and finish this line 13 | 14 | 15 | // 2. We want to use the `print_array` function to print coords...but coords isn't an array! 16 | // Create an array of type [f32; 2] and initialize it to contain the 17 | // information from coords. Uncomment the print_array line and run the code. 18 | // 19 | //let coords_arr... // create an array literal out of parts of `coord` here 20 | //print_array(coords_arr); // and pass it in here (this line doesn't need to change) 21 | 22 | 23 | let series = [1, 1, 2, 3, 5, 8, 13]; 24 | // 3. Make the `ding` function happy by passing it the value 13 out of the `series` array. 25 | // Use array indexing. Done correctly, `cargo run` will produce the additional output 26 | // "Ding, you found 13!" 27 | // 28 | //ding(...); 29 | 30 | 31 | let mess = ([3, 2], 3.14, [(false, -3), (true, -100)], 5, "candy"); 32 | // 4. Pass the `on_off` function the value `true` from the variable `mess`. Done correctly, 33 | // `cargo run` will produce the additional output "Lights are on!" I'll get you started: 34 | // 35 | //on_off(mess.2 ...); 36 | 37 | // 5. What a mess -- functions in a binary! Let's get organized! 38 | // 39 | // - Make a library file (src/lib.rs) 40 | // - Move all the functions (except main) into the library 41 | // - Make all the functions public with `pub` 42 | // - Bring all the functions into scope using use statements. Remember, the name of the library 43 | // is defined in Cargo.toml. You'll need to know that to `use` it. 44 | // 45 | // `cargo run` should produce the same output, only now the code is more organized. 🎉 46 | 47 | // Challenge: Uncomment the line below, run the code, and examine the 48 | // output. Then go refactor the print_distance() function according to the 49 | // instructions in the comments inside that function. 50 | 51 | // print_distance(coords); 52 | } 53 | 54 | fn print_difference(x: f32, y: f32) { 55 | println!("Difference between {} and {} is {}", x, y, (x - y).abs()); 56 | } 57 | 58 | fn print_array(a: [f32; 2]) { 59 | println!("The coordinates are ({}, {})", a[0], a[1]); 60 | } 61 | 62 | fn ding(x: i32) { 63 | if x == 13 { 64 | println!("Ding, you found 13!"); 65 | } 66 | } 67 | 68 | fn on_off(val: bool) { 69 | if val { 70 | println!("Lights are on!"); 71 | } 72 | } 73 | 74 | fn print_distance(z: (f32, f32)) { 75 | // Using z.0 and z.1 is not nearly as nice as using x and y. Lucky for 76 | // us, Rust supports destructuring function arguments. Try replacing "z" in 77 | // the parameter list above with "(x, y)" and then adjust the function 78 | // body to use x and y. 79 | println!( 80 | "Distance to the origin is {}", 81 | ( z.0.powf(2.0) + z.1.powf(2.0) ).sqrt()); 82 | } 83 | 84 | -------------------------------------------------------------------------------- /exercise/d_control_flow_strings/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "d_control_flow_strings" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /exercise/d_control_flow_strings/README.md: -------------------------------------------------------------------------------- 1 | Please clone this repository, change to this directory, and open `src/main.rs` 2 | and follow the instructions in the comments. 3 | -------------------------------------------------------------------------------- /exercise/d_control_flow_strings/src/main.rs: -------------------------------------------------------------------------------- 1 | // Silence some warnings so they don't distract from the exercise. 2 | #![allow(dead_code, unused_mut, unused_variables)] 3 | 4 | fn main() { 5 | // This collects any command-line arguments into a vector of Strings. 6 | // For example: 7 | // 8 | // cargo run apple banana 9 | // 10 | // ...produces the equivalent of 11 | // 12 | // vec!["apple".to_string(), "banana".to_string()] 13 | let args: Vec = std::env::args().skip(1).collect(); 14 | 15 | // This consumes the `args` vector to iterate through each String 16 | for arg in args { 17 | // 1a. Your task: handle the command-line arguments! 18 | // 19 | // - If arg is "sum", then call the sum() function 20 | // - If arg is "double", then call the double() function 21 | // - If arg is anything else, then call the count() function, passing "arg" to it. 22 | 23 | 24 | // 1b. Now try passing "sum", "double" and "bananas" to the program by adding your argument 25 | // after "cargo run". For example "cargo run sum" 26 | } 27 | } 28 | 29 | fn sum() { 30 | let mut sum = 0; 31 | // 2. Use a "for loop" to iterate through integers from 7 to 23 *inclusive* using a range 32 | // and add them all together (increment the `sum` variable). Hint: You should get 255 33 | // Run it with `cargo run sum` 34 | 35 | 36 | println!("The sum is {}", sum); 37 | } 38 | 39 | fn double() { 40 | let mut count = 0; 41 | let mut x = 1; 42 | // 3. Use a "while loop" to count how many times you can double the value of `x` (multiply `x` 43 | // by 2) until `x` is larger than 500. Increment `count` each time through the loop. Run it 44 | // with `cargo run double` Hint: The answer is 9 times. 45 | 46 | 47 | println!("You can double x {} times until x is larger than 500", count); 48 | } 49 | 50 | fn count(arg: String) { 51 | // Challenge: Use an unconditional loop (`loop`) to print `arg` 8 times, and then break. 52 | // You will need to count your loops, somehow. Run it with `cargo run bananas` 53 | // 54 | // print!("{} ", arg); // Execute this line 8 times, and then break. `print!` doesn't add a newline. 55 | 56 | 57 | println!(); // This will output just a newline at the end for cleanliness. 58 | } 59 | -------------------------------------------------------------------------------- /exercise/e_ownership_references/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "e_ownership_references" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /exercise/e_ownership_references/README.md: -------------------------------------------------------------------------------- 1 | Please clone this repository, change to this directory, and open `src/main.rs` 2 | and follow the instructions in the comments. 3 | -------------------------------------------------------------------------------- /exercise/e_ownership_references/src/main.rs: -------------------------------------------------------------------------------- 1 | // Silence some warnings so they don't distract from the exercise. 2 | #![allow(unused_mut, unused_variables)] 3 | 4 | fn main() { 5 | // This fancy stuff either gets the first argument as a String, or prints 6 | // usage and exits if an argument was not supplied to the program. 7 | let mut arg: String = std::env::args().nth(1).unwrap_or_else(|| { 8 | println!("Please supply an argument to this program."); 9 | std::process::exit(-1); 10 | }); 11 | 12 | // 1. Write a function `inspect` that takes a reference to a String, returns nothing, but 13 | // prints whether the contents of the String is plural or singular. Then uncomment and run this 14 | // code with `cargo run apple` and `cargo run apples'. Hint: use `.ends_with("s")` on the 15 | // String reference 16 | // 17 | //inspect(&arg); 18 | 19 | // 2. Write a function `change` that takes a *mutable* reference to a String and adds an "s" to 20 | // the String if it doesn't already end with "s". Then uncomment and run the code below with 21 | // `cargo run apple`. Hint: use `.push_str("s")` on the mutable String reference to add an "s". 22 | // 23 | //change(&mut arg); 24 | //println!("I have many {}", arg); 25 | 26 | // 3. Write a function `eat` that accepts ownership of (consumes) a String and returns a bool 27 | // indicating whether or not the String both starts with a "b" AND contains an "a". 28 | // Hint 1: use `.starts_with("b")` and `.contains("a")` 29 | // Hint 2: `&&` is the boolean "AND" operator 30 | // 31 | //if eat(arg) { 32 | // println!("Might be bananas"); 33 | //} else { 34 | // println!("Not bananas"); 35 | //} 36 | 37 | // Try running this program with "boat", "banana", and "grapes" as the arguments :-) 38 | 39 | // Challenge: Write a function "bedazzle" that takes a mutable reference to a String and 40 | // ignores what is in the string and replaces the contents of the string with the String 41 | // "sparkly". Then uncomment the code below. 42 | // 43 | // Hint: You will need to dereference the mutable reference in order to assign it a 44 | // new value. 45 | // 46 | // let mut material = "mud".to_string(); 47 | // println!("This material is just `{}`.", material); 48 | // bedazzle(&mut material); 49 | // println!("Wow! Now the material is `{}`!", material); 50 | } 51 | -------------------------------------------------------------------------------- /exercise/f_structs_traits/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "f_structs_traits" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | -------------------------------------------------------------------------------- /exercise/f_structs_traits/README.md: -------------------------------------------------------------------------------- 1 | Please clone this repository, change to this directory, and open `src/main.rs` 2 | and follow the instructions in the comments. 3 | -------------------------------------------------------------------------------- /exercise/f_structs_traits/src/main.rs: -------------------------------------------------------------------------------- 1 | // 1. Define a trait named `Bite` 2 | // 3 | // Define a single required method, `fn bite(self: &mut Self)`. We will call this method when we 4 | // want to bite something. Once this trait is defined, you should be able to run the program with 5 | // `cargo run` without any errors. 6 | // 7 | // trait Bite... 8 | 9 | 10 | // 2. Now create a struct named Grapes with a field that tracks how many grapes are left. If you 11 | // need a hint, look at how it was done for Carrot at the bottom of this file (you should probably 12 | // use a different field, though). 13 | // 14 | // #[derive(Debug)] // include this line right before your struct definition 15 | // struct Grapes... 16 | 17 | 18 | // 3. Implement Bite for Grapes. When you bite a Grapes, subtract 1 from how many grapes are left. 19 | // If you need a hint, look at how it was done for Carrot at the bottom of this file. 20 | // 21 | // impl Bite for... 22 | 23 | 24 | fn main() { 25 | // Once you finish #1 above, this part should work. 26 | let mut carrot = Carrot { percent_left: 100.0 }; 27 | carrot.bite(); 28 | println!("I take a bite: {:?}", carrot); 29 | 30 | // 4. Uncomment and adjust the code below to match how you defined your 31 | // Grapes struct. 32 | // 33 | //let mut grapes = Grapes { amount_left: 100 }; 34 | //grapes.bite(); 35 | //println!("Eat a grape: {:?}", grapes); 36 | 37 | // Challenge: Uncomment the code below. Create a generic `bunny_nibbles` 38 | // function that: 39 | // - takes a mutable reference to any type that implements Bite 40 | // - calls `.bite()` several times 41 | // Hint: Define the generic type between the function name and open paren: 42 | // fn function_name(...) 43 | // 44 | //bunny_nibbles(&mut carrot); 45 | //println!("Bunny nibbles for awhile: {:?}", carrot); 46 | } 47 | 48 | #[derive(Debug)] // This enables using the debugging format string "{:?}" 49 | struct Carrot { 50 | percent_left: f32, 51 | } 52 | 53 | impl Bite for Carrot { 54 | fn bite(self: &mut Self) { 55 | // Eat 20% of the remaining carrot. It may take awhile to eat it all... 56 | self.percent_left *= 0.8; 57 | } 58 | } -------------------------------------------------------------------------------- /exercise/g_collections_enums/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "g_collections_enums" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | rand = "0.8.5" 8 | -------------------------------------------------------------------------------- /exercise/g_collections_enums/README.md: -------------------------------------------------------------------------------- 1 | Please clone this repository, change to this directory, and open `src/main.rs` 2 | and follow the instructions in the comments. 3 | -------------------------------------------------------------------------------- /exercise/g_collections_enums/src/main.rs: -------------------------------------------------------------------------------- 1 | // Silence some warnings that could distract from the exercise 2 | #![allow(unused_variables, unused_mut, dead_code)] 3 | 4 | // Someone is shooting arrows at a target. We need to classify the shots. 5 | // 6 | // 1a. Create an enum called `Shot` with variants: 7 | // - `Bullseye` 8 | // - `Hit`, containing the distance from the center (an f64) 9 | // - `Miss` 10 | // 11 | // You will need to complete 1b as well before you will be able to run this program successfully. 12 | 13 | impl Shot { 14 | // Here is a method for the `Shot` enum you just defined. 15 | fn points(self) -> i32 { 16 | // 1b. Implement this method to convert a Shot into points 17 | // - return 5 points if `self` is a `Shot::Bullseye` 18 | // - return 2 points if `self` is a `Shot::Hit(x)` where x < 3.0 19 | // - return 1 point if `self` is a `Shot::Hit(x)` where x >= 3.0 20 | // - return 0 points if `self` is a Miss 21 | } 22 | } 23 | 24 | fn main() { 25 | // Simulate shooting a bunch of arrows and gathering their coordinates on the target. 26 | let arrow_coords: Vec = get_arrow_coords(5); 27 | let mut shots: Vec = Vec::new(); 28 | 29 | // 2. For each coord in arrow_coords: 30 | // 31 | // A. Call `coord.print_description()` 32 | // B. Append the correct variant of `Shot` to the `shots` vector depending on the value of 33 | // `coord.distance_from_center()` 34 | // - Less than 1.0 -- `Shot::Bullseye` 35 | // - Between 1.0 and 5.0 -- `Shot::Hit(value)` 36 | // - Greater than 5.0 -- `Shot::Miss` 37 | 38 | 39 | let mut total = 0; 40 | // 3. Finally, loop through each shot in shots and add its points to total 41 | 42 | println!("Final point total is: {}", total); 43 | } 44 | 45 | // A coordinate of where an Arrow hit 46 | #[derive(Debug)] 47 | struct Coord { 48 | x: f64, 49 | y: f64, 50 | } 51 | 52 | impl Coord { 53 | fn distance_from_center(&self) -> f64 { 54 | (self.x.powf(2.0) + self.y.powf(2.0)).sqrt() 55 | } 56 | fn print_description(&self) { 57 | println!( 58 | "coord is {:.1} away, at ({:.1}, {:.1})", 59 | self.distance_from_center(), 60 | self.x, 61 | self.y); 62 | } 63 | 64 | } 65 | 66 | // Generate some random coordinates 67 | fn get_arrow_coords(num: u32) -> Vec { 68 | let mut coords: Vec = Vec::new(); 69 | for _ in 0..num { 70 | let coord = Coord { 71 | x: (rand::random::() - 0.5) * 12.0, 72 | y: (rand::random::() - 0.5) * 12.0, 73 | }; 74 | coords.push(coord); 75 | } 76 | coords 77 | } -------------------------------------------------------------------------------- /exercise/h_closures_threads/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "h_closures_threads" 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 | crossbeam = "0.8.2" 10 | -------------------------------------------------------------------------------- /exercise/h_closures_threads/src/main.rs: -------------------------------------------------------------------------------- 1 | // Silence some warnings so they don't distract from the exercise. 2 | #![allow(dead_code, unused_imports, unused_variables)] 3 | use crossbeam::channel; 4 | use std::thread; 5 | use std::time::Duration; 6 | 7 | fn expensive_sum(v: Vec) -> i32 { 8 | pause_ms(500); 9 | println!("Child thread: just about finished"); 10 | // 1a. Between the .iter() and the .sum() add a .filter() with a closure to keep any even 11 | // number (`x % 2` will be 0 for even numbers). 12 | // 1b. Between the .filter() and the .sum() add a .map() with a closure to square the values 13 | // (multiply them by themselves) 14 | // 15 | // In the closures for both the .filter() and .map() the argument will be a reference, so you'll 16 | // either need to dereference the argument once in the parameter list like this: `|&x|` or you 17 | // will need to dereference it each time you use it in the expression like this: `*x` 18 | v.iter() 19 | // .filter() goes here 20 | // .map() goes here 21 | .sum() 22 | } 23 | 24 | fn pause_ms(ms: u64) { 25 | thread::sleep(Duration::from_millis(ms)); 26 | } 27 | 28 | fn main() { 29 | let my_vector = vec![2, 5, 1, 0, 4, 3]; 30 | 31 | // 2. Spawn a child thread and have it call `expensive_sum(my_vector)`. Store the returned 32 | // join handle in a variable called `handle`. Once you've done this you should be able to run 33 | // the code and see the Child thread output in the middle of the main thread's letters 34 | // 35 | //let handle = ... 36 | 37 | // While the child thread is running, the main thread will also do some work 38 | for letter in vec!["a", "b", "c", "d", "e", "f"] { 39 | println!("Main thread: Letter {}", letter); 40 | pause_ms(200); 41 | } 42 | 43 | // 3. Let's retrieve the value returned by the child thread once it has exited. Using the 44 | // `handle` variable you stored the join handle in earlier, call .join() to wait for the thread 45 | // to exit with a `Result`. Get the i32 out of the result and store it in a `sum` 46 | // variable. Uncomment the println. If you did 1a and 1b correctly, the sum should be 20. 47 | // 48 | //let sum = 49 | //println!("The child thread's expensive sum is {}", sum); 50 | 51 | // Time for some fun with threads and channels! Though there is a primitive type of channel 52 | // in the std::sync::mpsc module, I recommend always using channels from the crossbeam crate, 53 | // which is what we will use here. 54 | // 55 | // 4. Uncomment the block comment below (Find and remove the `/*` and `*/`). Examine how the 56 | // flow of execution works. Once you understand it, alter the values passed to the `pause_ms()` 57 | // calls so that both the "Thread B" outputs occur before the "Thread A" outputs. 58 | 59 | /* 60 | let (tx, rx) = channel::unbounded(); 61 | // Cloning a channel makes another variable connected to that end of the channel so that you can 62 | // send it to another thread. 63 | let tx2 = tx.clone(); 64 | 65 | let handle_a = thread::spawn(move || { 66 | pause_ms(0); 67 | tx2.send("Thread A: 1").unwrap(); 68 | pause_ms(200); 69 | tx2.send("Thread A: 2").unwrap(); 70 | }); 71 | 72 | pause_ms(100); // Make sure Thread A has time to get going before we spawn Thread B 73 | 74 | let handle_b = thread::spawn(move || { 75 | pause_ms(0); 76 | tx.send("Thread B: 1").unwrap(); 77 | pause_ms(200); 78 | tx.send("Thread B: 2").unwrap(); 79 | }); 80 | 81 | // Using a Receiver channel as an iterator is a convenient way to get values until the channel 82 | // gets closed. A Receiver channel is automatically closed once all Sender channels have been 83 | // closed. Both our threads automatically close their Sender channels when they exit and the 84 | // destructors for the channels get automatically called. 85 | for msg in rx { 86 | println!("Main thread: Received {}", msg); 87 | } 88 | 89 | // Join the child threads for good hygiene. 90 | handle_a.join().unwrap(); 91 | handle_b.join().unwrap(); 92 | */ 93 | 94 | // Challenge: Make two child threads and give them each a receiving end to a channel. From the 95 | // main thread loop through several values and print each out and then send it to the channel. 96 | // On the child threads print out the values you receive. Close the sending side in the main 97 | // thread by calling `drop(tx)` (assuming you named your sender channel variable `tx`). Join 98 | // the child threads. 99 | println!("Main thread: Exiting.") 100 | } 101 | -------------------------------------------------------------------------------- /exercise/z_final_project/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mirage" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | image = "0.24.3" 8 | num-complex = "0.4.2" 9 | -------------------------------------------------------------------------------- /exercise/z_final_project/README.md: -------------------------------------------------------------------------------- 1 | Please clone this repository, change to this directory, and open `src/main.rs` 2 | and follow the instructions in the comments. 3 | -------------------------------------------------------------------------------- /exercise/z_final_project/dyson.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CleanCut/ultimate_rust_crash_course/6f923ed7b9c2a9ea6e56a7335a67c59fbe926587/exercise/z_final_project/dyson.png -------------------------------------------------------------------------------- /exercise/z_final_project/pens.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CleanCut/ultimate_rust_crash_course/6f923ed7b9c2a9ea6e56a7335a67c59fbe926587/exercise/z_final_project/pens.png -------------------------------------------------------------------------------- /exercise/z_final_project/src/main.rs: -------------------------------------------------------------------------------- 1 | // FINAL PROJECT 2 | // 3 | // Create an image processing application. Exactly what it does and how it does 4 | // it is up to you, though I've stubbed a good amount of suggestions for you. 5 | // Look for comments labeled **OPTION** below. 6 | // 7 | // Two image files are included in the project root for your convenience: dyson.png and pens.png 8 | // Feel free to use them or provide (or generate) your own images. 9 | // 10 | // Don't forget to have fun and play around with the code! 11 | // 12 | // Documentation for the image library is here: https://docs.rs/image/0.21.0/image/ 13 | // 14 | // NOTE 1: Image processing is very CPU-intensive. Your program will run *noticeably* faster if you 15 | // run it with the `--release` flag. 16 | // 17 | // cargo run --release [ARG1 [ARG2]] 18 | // 19 | // For example: 20 | // 21 | // cargo run --release blur image.png blurred.png 22 | // 23 | // NOTE 2: This is how you parse a number from a string (or crash with a 24 | // message). It works with any integer or float type. 25 | // 26 | // let positive_number: u32 = some_string.parse().expect("Failed to parse a number"); 27 | 28 | fn main() { 29 | // 1. First, you need to implement some basic command-line argument handling 30 | // so you can make your program do different things. Here's a little bit 31 | // to get you started doing manual parsing. 32 | // 33 | // Challenge: If you're feeling really ambitious, you could delete this code 34 | // and use the "clap" library instead: https://docs.rs/clap/2.32.0/clap/ 35 | let mut args: Vec = std::env::args().skip(1).collect(); 36 | if args.is_empty() { 37 | print_usage_and_exit(); 38 | } 39 | let subcommand = args.remove(0); 40 | match subcommand.as_str() { 41 | // EXAMPLE FOR CONVERSION OPERATIONS 42 | "blur" => { 43 | if args.len() != 2 { 44 | print_usage_and_exit(); 45 | } 46 | let infile = args.remove(0); 47 | let outfile = args.remove(0); 48 | // **OPTION** 49 | // Improve the blur implementation -- see the blur() function below 50 | blur(infile, outfile); 51 | } 52 | 53 | // **OPTION** 54 | // Brighten -- see the brighten() function below 55 | 56 | // **OPTION** 57 | // Crop -- see the crop() function below 58 | 59 | // **OPTION** 60 | // Rotate -- see the rotate() function below 61 | 62 | // **OPTION** 63 | // Invert -- see the invert() function below 64 | 65 | // **OPTION** 66 | // Grayscale -- see the grayscale() function below 67 | 68 | // A VERY DIFFERENT EXAMPLE...a really fun one. :-) 69 | "fractal" => { 70 | if args.len() != 1 { 71 | print_usage_and_exit(); 72 | } 73 | let outfile = args.remove(0); 74 | fractal(outfile); 75 | } 76 | 77 | // **OPTION** 78 | // Generate -- see the generate() function below -- this should be sort of like "fractal()"! 79 | 80 | // For everything else... 81 | _ => { 82 | print_usage_and_exit(); 83 | } 84 | } 85 | } 86 | 87 | fn print_usage_and_exit() { 88 | println!("USAGE (when in doubt, use a .png extension on your filenames)"); 89 | println!("blur INFILE OUTFILE"); 90 | println!("fractal OUTFILE"); 91 | // **OPTION** 92 | // Print useful information about what subcommands and arguments you can use 93 | // println!("..."); 94 | std::process::exit(-1); 95 | } 96 | 97 | fn blur(infile: String, outfile: String) { 98 | // Here's how you open an existing image file 99 | let img = image::open(infile).expect("Failed to open INFILE."); 100 | // **OPTION** 101 | // Parse the blur amount (an f32) from the command-line and pass it through 102 | // to this function, instead of hard-coding it to 2.0. 103 | let img2 = img.blur(2.0); 104 | // Here's how you save an image to a file. 105 | img2.save(outfile).expect("Failed writing OUTFILE."); 106 | } 107 | 108 | fn brighten(infile: String, outfile: String) { 109 | // See blur() for an example of how to open / save an image. 110 | 111 | // .brighten() takes one argument, an i32. Positive numbers brighten the 112 | // image. Negative numbers darken it. It returns a new image. 113 | 114 | // Challenge: parse the brightness amount from the command-line and pass it 115 | // through to this function. 116 | } 117 | 118 | fn crop(infile: String, outfile: String) { 119 | // See blur() for an example of how to open an image. 120 | 121 | // .crop() takes four arguments: x: u32, y: u32, width: u32, height: u32 122 | // You may hard-code them, if you like. It returns a new image. 123 | 124 | // Challenge: parse the four values from the command-line and pass them 125 | // through to this function. 126 | 127 | // See blur() for an example of how to save the image. 128 | } 129 | 130 | fn rotate(infile: String, outfile: String) { 131 | // See blur() for an example of how to open an image. 132 | 133 | // There are 3 rotate functions to choose from (all clockwise): 134 | // .rotate90() 135 | // .rotate180() 136 | // .rotate270() 137 | // All three methods return a new image. Pick one and use it! 138 | 139 | // Challenge: parse the rotation amount from the command-line, pass it 140 | // through to this function to select which method to call. 141 | 142 | // See blur() for an example of how to save the image. 143 | } 144 | 145 | fn invert(infile: String, outfile: String) { 146 | // See blur() for an example of how to open an image. 147 | 148 | // .invert() takes no arguments and converts the image in-place, so you 149 | // will use the same image to save out to a different file. 150 | 151 | // See blur() for an example of how to save the image. 152 | } 153 | 154 | fn grayscale(infile: String, outfile: String) { 155 | // See blur() for an example of how to open an image. 156 | 157 | // .grayscale() takes no arguments. It returns a new image. 158 | 159 | // See blur() for an example of how to save the image. 160 | } 161 | 162 | fn generate(outfile: String) { 163 | // Create an ImageBuffer -- see fractal() for an example 164 | 165 | // Iterate over the coordinates and pixels of the image -- see fractal() for an example 166 | 167 | // Set the image to some solid color. -- see fractal() for an example 168 | 169 | // Challenge: parse some color data from the command-line, pass it through 170 | // to this function to use for the solid color. 171 | 172 | // Challenge 2: Generate something more interesting! 173 | 174 | // See blur() for an example of how to save the image 175 | } 176 | 177 | // This code was adapted from https://github.com/PistonDevelopers/image 178 | fn fractal(outfile: String) { 179 | let width = 800; 180 | let height = 800; 181 | 182 | let mut imgbuf = image::ImageBuffer::new(width, height); 183 | 184 | let scale_x = 3.0 / width as f32; 185 | let scale_y = 3.0 / height as f32; 186 | 187 | // Iterate over the coordinates and pixels of the image 188 | for (x, y, pixel) in imgbuf.enumerate_pixels_mut() { 189 | // Use red and blue to be a pretty gradient background 190 | let red = (0.3 * x as f32) as u8; 191 | let blue = (0.3 * y as f32) as u8; 192 | 193 | // Use green as the fractal foreground (here is the fractal math part) 194 | let cx = y as f32 * scale_x - 1.5; 195 | let cy = x as f32 * scale_y - 1.5; 196 | 197 | let c = num_complex::Complex::new(-0.4, 0.6); 198 | let mut z = num_complex::Complex::new(cx, cy); 199 | 200 | let mut green = 0; 201 | while green < 255 && z.norm() <= 2.0 { 202 | z = z * z + c; 203 | green += 1; 204 | } 205 | 206 | // Actually set the pixel. red, green, and blue are u8 values! 207 | *pixel = image::Rgb([red, green, blue]); 208 | } 209 | 210 | imgbuf.save(outfile).unwrap(); 211 | } 212 | 213 | // **SUPER CHALLENGE FOR LATER** - Let's face it, you don't have time for this during class. 214 | // 215 | // Make all of the subcommands stackable! 216 | // 217 | // For example, if you run: 218 | // 219 | // cargo run infile.png outfile.png blur 2.5 invert rotate 180 brighten 10 220 | // 221 | // ...then your program would: 222 | // - read infile.png 223 | // - apply a blur of 2.5 224 | // - invert the colors 225 | // - rotate the image 180 degrees clockwise 226 | // - brighten the image by 10 227 | // - and write the result to outfile.png 228 | // 229 | // Good luck! 230 | --------------------------------------------------------------------------------