diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 00000000..bf2a041a --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,20 @@ +name: Rustlings Tests + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Build + run: cargo build --verbose + - name: Run tests + run: cargo test --verbose diff --git a/.gitignore b/.gitignore index 253d85bd..534453bc 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ rust-project.json .idea .vscode *.iml +*.o diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2ca0e341..cc8ac923 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,7 +21,7 @@ _implement a new feature! ➡️ [open an Issue to discuss it first, then a Pull `rustlings` is basically a glorified `rustc` wrapper. Therefore the source code isn't really that complicated since the bulk of the work is done by `rustc`. -`src/main.rs` contains a simple `clap` CLI that loads from `src/verify.rs` and `src/run.rs`. +`src/main.rs` contains a simple `argh` CLI that connects to most of the other source files. ### Adding an exercise @@ -29,7 +29,7 @@ isn't really that complicated since the bulk of the work is done by `rustc`. The first step is to add the exercise! Name the file `exercises/yourTopic/yourTopicN.rs`, make sure to put in some helpful links, and link to sections of the book in `exercises/yourTopic/README.md`. -Next make sure it runs with `rustlings`. The exercise metadata is stored in `info.toml`, under the `exercises` array. The order of the `exercises` array determines the order the exercises are run by `rustlings verify`. +Next make sure it runs with `rustlings`. The exercise metadata is stored in `info.toml`, under the `exercises` array. The order of the `exercises` array determines the order the exercises are run by `rustlings verify` and `rustlings watch`. Add the metadata for your exercise in the correct order in the `exercises` array. If you are unsure of the correct ordering, add it at the bottom and ask in your pull request. The exercise metadata should contain the following: ```diff @@ -43,7 +43,7 @@ Add the metadata for your exercise in the correct order in the `exercises` array ... ``` -The `mode` attribute decides whether Rustlings will only compile your exercise, or compile and test it. If you have tests to verify in your exercise, choose `test`, otherwise `compile`. +The `mode` attribute decides whether Rustlings will only compile your exercise, or compile and test it. If you have tests to verify in your exercise, choose `test`, otherwise `compile`. If you're working on a Clippy exercise, use `mode = "clippy"`. That's all! Feel free to put up a pull request. @@ -67,19 +67,19 @@ changes. There's a couple of things to watch out for: #### Write correct commit messages We follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0-beta.4/) -specification, because it makes it easier to generate changelogs automatically. +specification. This means that you have to format your commit messages in a specific way. Say you're working on adding a new exercise called `foobar1.rs`. You could write the following commit message: ``` -feat: Add foobar1.rs exercise +feat: add foobar1.rs exercise ``` If you're just fixing a bug, please use the `fix` type: ``` -fix(verify): Make sure verify doesn't self-destruct +fix(verify): make sure verify doesn't self-destruct ``` The scope within the brackets is optional, but should be any of these: @@ -96,7 +96,7 @@ When the commit also happens to close an existing issue, link it in the message body: ``` -fix: Update foobar +fix: update foobar closes #101029908 ``` @@ -104,13 +104,13 @@ closes #101029908 If you're doing simple changes, like updating a book link, use `chore`: ``` -chore: Update exercise1.rs book link +chore: update exercise1.rs book link ``` If you're updating documentation, use `docs`: ``` -docs: Add more information to Readme +docs: add more information to Readme ``` If, and only if, you're absolutely sure you want to make a breaking change @@ -118,7 +118,7 @@ If, and only if, you're absolutely sure you want to make a breaking change explain the breaking change in the message body: ``` -fix!: Completely change verification +fix!: completely change verification BREAKING CHANGE: This has to be done because lorem ipsum dolor ``` @@ -126,6 +126,5 @@ BREAKING CHANGE: This has to be done because lorem ipsum dolor #### Pull Request Workflow Once you open a Pull Request, it may be reviewed or labeled (or both) until -the maintainers accept your change. Then, [bors](https://github.com/bors) will -run the test suite with your changes and if it's successful, automatically -merge it in! +the maintainers accept your change. Please be patient, it may take some time +for this to happen! diff --git a/Cargo.lock b/Cargo.lock index 6737d29c..df190ad8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -459,7 +459,7 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" [[package]] name = "rustlings" -version = "4.7.1" +version = "4.8.0" dependencies = [ "argh", "assert_cmd", diff --git a/Cargo.toml b/Cargo.toml index e356a2c2..8a3264d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rustlings" version = "4.8.0" -authors = ["mokou ", "Carol (Nichols || Goulding) "] +authors = ["Liv ", "Carol (Nichols || Goulding) "] edition = "2021" [dependencies] @@ -24,6 +24,3 @@ path = "src/main.rs" assert_cmd = "0.11.0" predicates = "1.0.1" glob = "0.3.0" - -[features] -exercises = [] diff --git a/README.md b/README.md index 12a77810..103aec8b 100644 --- a/README.md +++ b/README.md @@ -44,9 +44,9 @@ Start-BitsTransfer -Source https://raw.githubusercontent.com/rust-lang/rustlings To install Rustlings. Same as on MacOS/Linux, you will have access to the `rustlings` command after it. -When you get a permission denied message then you have to exclude the directory where you placed the rustlings in your virus-scanner +If you get a permission denied message, you might have to exclude the directory where you cloned Rustlings in your antivirus. -## Browser: +## Browser [Run on Repl.it](https://repl.it/github/rust-lang/rustlings) @@ -150,24 +150,6 @@ cargo uninstall rustlings Now you should be done! -## Completion - -Rustlings isn't done; there are a couple of sections that are very experimental and don't have proper documentation. These include: - -- Errors (`exercises/errors/`) -- Option (`exercises/option/`) -- Result (`exercises/result/`) -- Move Semantics (could still be improved, `exercises/move_semantics/`) - -Additionally, we could use exercises on a couple of topics: - -- Structs -- Better ownership stuff -- `impl` -- ??? probably more - -If you are interested in improving or adding new ones, please feel free to contribute! Read on for more information :) - ## Contributing See [CONTRIBUTING.md](./CONTRIBUTING.md). diff --git a/exercises/advanced_errors/advanced_errs1.rs b/exercises/advanced_errors/advanced_errs1.rs deleted file mode 100644 index 4bc7b635..00000000 --- a/exercises/advanced_errors/advanced_errs1.rs +++ /dev/null @@ -1,98 +0,0 @@ -// advanced_errs1.rs - -// Remember back in errors6, we had multiple mapping functions so that we -// could translate lower-level errors into our custom error type using -// `map_err()`? What if we could use the `?` operator directly instead? - -// Make this code compile! Execute `rustlings hint advanced_errs1` for -// hints :) - -// I AM NOT DONE - -use std::num::ParseIntError; -use std::str::FromStr; - -// This is a custom error type that we will be using in the `FromStr` -// implementation. -#[derive(PartialEq, Debug)] -enum ParsePosNonzeroError { - Creation(CreationError), - ParseInt(ParseIntError), -} - -impl From for ParsePosNonzeroError { - fn from(e: CreationError) -> Self { - // TODO: complete this implementation so that the `?` operator will - // work for `CreationError` - } -} - -// TODO: implement another instance of the `From` trait here so that the -// `?` operator will work in the other place in the `FromStr` -// implementation below. - -// Don't change anything below this line. - -impl FromStr for PositiveNonzeroInteger { - type Err = ParsePosNonzeroError; - fn from_str(s: &str) -> Result { - let x: i64 = s.parse()?; - Ok(PositiveNonzeroInteger::new(x)?) - } -} - -#[derive(PartialEq, Debug)] -struct PositiveNonzeroInteger(u64); - -#[derive(PartialEq, Debug)] -enum CreationError { - Negative, - Zero, -} - -impl PositiveNonzeroInteger { - fn new(value: i64) -> Result { - match value { - x if x < 0 => Err(CreationError::Negative), - x if x == 0 => Err(CreationError::Zero), - x => Ok(PositiveNonzeroInteger(x as u64)), - } - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_parse_error() { - // We can't construct a ParseIntError, so we have to pattern match. - assert!(matches!( - PositiveNonzeroInteger::from_str("not a number"), - Err(ParsePosNonzeroError::ParseInt(_)) - )); - } - - #[test] - fn test_negative() { - assert_eq!( - PositiveNonzeroInteger::from_str("-555"), - Err(ParsePosNonzeroError::Creation(CreationError::Negative)) - ); - } - - #[test] - fn test_zero() { - assert_eq!( - PositiveNonzeroInteger::from_str("0"), - Err(ParsePosNonzeroError::Creation(CreationError::Zero)) - ); - } - - #[test] - fn test_positive() { - let x = PositiveNonzeroInteger::new(42); - assert!(x.is_ok()); - assert_eq!(PositiveNonzeroInteger::from_str("42"), Ok(x.unwrap())); - } -} diff --git a/exercises/advanced_errors/advanced_errs2.rs b/exercises/advanced_errors/advanced_errs2.rs deleted file mode 100644 index 54e669fd..00000000 --- a/exercises/advanced_errors/advanced_errs2.rs +++ /dev/null @@ -1,202 +0,0 @@ -// advanced_errs2.rs - -// This exercise demonstrates a few traits that are useful for custom error -// types to implement, especially so that other code can consume the custom -// error type more usefully. - -// Make this compile, and make the tests pass! -// Execute `rustlings hint advanced_errs2` for hints. - -// Steps: -// 1. Implement a missing trait so that `main()` will compile. -// 2. Complete the partial implementation of `From` for -// `ParseClimateError`. -// 3. Handle the missing error cases in the `FromStr` implementation for -// `Climate`. -// 4. Complete the partial implementation of `Display` for -// `ParseClimateError`. - -// I AM NOT DONE - -use std::error::Error; -use std::fmt::{self, Display, Formatter}; -use std::num::{ParseFloatError, ParseIntError}; -use std::str::FromStr; - -// This is the custom error type that we will be using for the parser for -// `Climate`. -#[derive(Debug, PartialEq)] -enum ParseClimateError { - Empty, - BadLen, - NoCity, - ParseInt(ParseIntError), - ParseFloat(ParseFloatError), -} - -// This `From` implementation allows the `?` operator to work on -// `ParseIntError` values. -impl From for ParseClimateError { - fn from(e: ParseIntError) -> Self { - Self::ParseInt(e) - } -} - -// This `From` implementation allows the `?` operator to work on -// `ParseFloatError` values. -impl From for ParseClimateError { - fn from(e: ParseFloatError) -> Self { - // TODO: Complete this function - } -} - -// TODO: Implement a missing trait so that `main()` below will compile. It -// is not necessary to implement any methods inside the missing trait. - -// The `Display` trait allows for other code to obtain the error formatted -// as a user-visible string. -impl Display for ParseClimateError { - // TODO: Complete this function so that it produces the correct strings - // for each error variant. - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - // Imports the variants to make the following code more compact. - use ParseClimateError::*; - match self { - NoCity => write!(f, "no city name"), - ParseFloat(e) => write!(f, "error parsing temperature: {}", e), - } - } -} - -#[derive(Debug, PartialEq)] -struct Climate { - city: String, - year: u32, - temp: f32, -} - -// Parser for `Climate`. -// 1. Split the input string into 3 fields: city, year, temp. -// 2. Return an error if the string is empty or has the wrong number of -// fields. -// 3. Return an error if the city name is empty. -// 4. Parse the year as a `u32` and return an error if that fails. -// 5. Parse the temp as a `f32` and return an error if that fails. -// 6. Return an `Ok` value containing the completed `Climate` value. -impl FromStr for Climate { - type Err = ParseClimateError; - // TODO: Complete this function by making it handle the missing error - // cases. - fn from_str(s: &str) -> Result { - let v: Vec<_> = s.split(',').collect(); - let (city, year, temp) = match &v[..] { - [city, year, temp] => (city.to_string(), year, temp), - _ => return Err(ParseClimateError::BadLen), - }; - let year: u32 = year.parse()?; - let temp: f32 = temp.parse()?; - Ok(Climate { city, year, temp }) - } -} - -// Don't change anything below this line (other than to enable ignored -// tests). - -fn main() -> Result<(), Box> { - println!("{:?}", "Hong Kong,1999,25.7".parse::()?); - println!("{:?}", "".parse::()?); - Ok(()) -} - -#[cfg(test)] -mod test { - use super::*; - #[test] - fn test_empty() { - let res = "".parse::(); - assert_eq!(res, Err(ParseClimateError::Empty)); - assert_eq!(res.unwrap_err().to_string(), "empty input"); - } - #[test] - fn test_short() { - let res = "Boston,1991".parse::(); - assert_eq!(res, Err(ParseClimateError::BadLen)); - assert_eq!(res.unwrap_err().to_string(), "incorrect number of fields"); - } - #[test] - fn test_long() { - let res = "Paris,1920,17.2,extra".parse::(); - assert_eq!(res, Err(ParseClimateError::BadLen)); - assert_eq!(res.unwrap_err().to_string(), "incorrect number of fields"); - } - #[test] - fn test_no_city() { - let res = ",1997,20.5".parse::(); - assert_eq!(res, Err(ParseClimateError::NoCity)); - assert_eq!(res.unwrap_err().to_string(), "no city name"); - } - #[test] - fn test_parse_int_neg() { - let res = "Barcelona,-25,22.3".parse::(); - assert!(matches!(res, Err(ParseClimateError::ParseInt(_)))); - let err = res.unwrap_err(); - if let ParseClimateError::ParseInt(ref inner) = err { - assert_eq!( - err.to_string(), - format!("error parsing year: {}", inner.to_string()) - ); - } else { - unreachable!(); - }; - } - #[test] - fn test_parse_int_bad() { - let res = "Beijing,foo,15.0".parse::(); - assert!(matches!(res, Err(ParseClimateError::ParseInt(_)))); - let err = res.unwrap_err(); - if let ParseClimateError::ParseInt(ref inner) = err { - assert_eq!( - err.to_string(), - format!("error parsing year: {}", inner.to_string()) - ); - } else { - unreachable!(); - }; - } - #[test] - fn test_parse_float() { - let res = "Manila,2001,bar".parse::(); - assert!(matches!(res, Err(ParseClimateError::ParseFloat(_)))); - let err = res.unwrap_err(); - if let ParseClimateError::ParseFloat(ref inner) = err { - assert_eq!( - err.to_string(), - format!("error parsing temperature: {}", inner.to_string()) - ); - } else { - unreachable!(); - }; - } - #[test] - fn test_parse_good() { - let res = "Munich,2015,23.1".parse::(); - assert_eq!( - res, - Ok(Climate { - city: "Munich".to_string(), - year: 2015, - temp: 23.1, - }) - ); - } - #[test] - #[ignore] - fn test_downcast() { - let res = "São Paulo,-21,28.5".parse::(); - assert!(matches!(res, Err(ParseClimateError::ParseInt(_)))); - let err = res.unwrap_err(); - let inner: Option<&(dyn Error + 'static)> = err.source(); - assert!(inner.is_some()); - assert!(inner.unwrap().is::()); - } -} diff --git a/exercises/advanced_errors/mod.rs b/exercises/advanced_errors/mod.rs deleted file mode 100644 index e33fb80d..00000000 --- a/exercises/advanced_errors/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod advanced_errs1; -mod advanced_errs2; diff --git a/exercises/clippy/clippy1.rs b/exercises/clippy/clippy1.rs index c5f84a9c..bad46891 100644 --- a/exercises/clippy/clippy1.rs +++ b/exercises/clippy/clippy1.rs @@ -4,7 +4,7 @@ // // For these exercises the code will fail to compile when there are clippy warnings // check clippy's suggestions from the output to solve the exercise. -// Execute `rustlings hint clippy1` for hints :) +// Execute `rustlings hint clippy1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/clippy/clippy2.rs b/exercises/clippy/clippy2.rs index 37af9ed0..dac40dbe 100644 --- a/exercises/clippy/clippy2.rs +++ b/exercises/clippy/clippy2.rs @@ -1,5 +1,5 @@ // clippy2.rs -// Make me compile! Execute `rustlings hint clippy2` for hints :) +// Execute `rustlings hint clippy2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/clippy/clippy3.rs b/exercises/clippy/clippy3.rs new file mode 100644 index 00000000..b0159ebe --- /dev/null +++ b/exercises/clippy/clippy3.rs @@ -0,0 +1,28 @@ +// clippy3.rs +// Here's a couple more easy Clippy fixes, so you can see its utility. + +// I AM NOT DONE + +#[allow(unused_variables, unused_assignments)] +fn main() { + let my_option: Option<()> = None; + if my_option.is_none() { + my_option.unwrap(); + } + + let my_arr = &[ + -1, -2, -3 + -4, -5, -6 + ]; + println!("My array! Here it is: {:?}", my_arr); + + let my_empty_vec = vec![1, 2, 3, 4, 5].resize(0, 5); + println!("This Vec is empty, see? {:?}", my_empty_vec); + + let mut value_a = 45; + let mut value_b = 66; + // Let's swap these two! + value_a = value_b; + value_b = value_a; + println!("value a: {}; value b: {}", value_a, value_b); +} diff --git a/exercises/clippy/mod.rs b/exercises/clippy/mod.rs deleted file mode 100644 index 689dc95f..00000000 --- a/exercises/clippy/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod clippy1; -mod clippy2; diff --git a/exercises/collections/README.md b/exercises/collections/README.md deleted file mode 100644 index b6d62acb..00000000 --- a/exercises/collections/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Collections - -Rust’s standard library includes a number of very useful data -structures called collections. Most other data types represent one -specific value, but collections can contain multiple values. Unlike -the built-in array and tuple types, the data these collections point -to is stored on the heap, which means the amount of data does not need -to be known at compile time and can grow or shrink as the program -runs. - -This exercise will get you familiar with two fundamental data -structures that are used very often in Rust programs: - -* A *vector* allows you to store a variable number of values next to - each other. -* A *hash map* allows you to associate a value with a particular key. - You may also know this by the names [*unordered map* in C++](https://en.cppreference.com/w/cpp/container/unordered_map), - [*dictionary* in Python](https://docs.python.org/3/tutorial/datastructures.html#dictionaries) or an *associative array* in other languages. - -## Further information - -- [Storing Lists of Values with Vectors](https://doc.rust-lang.org/stable/book/ch08-01-vectors.html) -- [Storing Keys with Associated Values in Hash Maps](https://doc.rust-lang.org/book/ch08-03-hash-maps.html) diff --git a/exercises/collections/mod.rs b/exercises/collections/mod.rs deleted file mode 100644 index f46c1424..00000000 --- a/exercises/collections/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod hashmap1; -mod hashmap2; -mod vec1; -mod vec2; diff --git a/exercises/conversions/as_ref_mut.rs b/exercises/conversions/as_ref_mut.rs index 84f4a60c..9f479739 100644 --- a/exercises/conversions/as_ref_mut.rs +++ b/exercises/conversions/as_ref_mut.rs @@ -1,6 +1,7 @@ // AsRef and AsMut allow for cheap reference-to-reference conversions. // Read more about them at https://doc.rust-lang.org/std/convert/trait.AsRef.html // and https://doc.rust-lang.org/std/convert/trait.AsMut.html, respectively. +// Execute `rustlings hint as_ref_mut` or use the `hint` watch subcommand for a hint. // I AM NOT DONE @@ -16,10 +17,10 @@ fn char_counter(arg: T) -> usize { arg.as_ref().chars().count() } -fn main() { - let s = "Café au lait"; - println!("{}", char_counter(s)); - println!("{}", byte_counter(s)); +// Squares a number using AsMut. Add the trait bound as is appropriate and +// implement the function body. +fn num_sq(arg: &mut T) { + ??? } #[cfg(test)] @@ -49,4 +50,11 @@ mod tests { let s = String::from("Cafe au lait"); assert_eq!(char_counter(s.clone()), byte_counter(s)); } + + #[test] + fn mult_box() { + let mut num: Box = Box::new(3); + num_sq(&mut num); + assert_eq!(*num, 9); + } } diff --git a/exercises/conversions/from_into.rs b/exercises/conversions/from_into.rs index 9d84174d..6c272c3b 100644 --- a/exercises/conversions/from_into.rs +++ b/exercises/conversions/from_into.rs @@ -1,6 +1,8 @@ // The From trait is used for value-to-value conversions. // If From is implemented correctly for a type, the Into trait should work conversely. // You can read more about it at https://doc.rust-lang.org/std/convert/trait.From.html +// Execute `rustlings hint from_into` or use the `hint` watch subcommand for a hint. + #[derive(Debug)] struct Person { name: String, diff --git a/exercises/conversions/from_str.rs b/exercises/conversions/from_str.rs index ece0b3cf..fe168159 100644 --- a/exercises/conversions/from_str.rs +++ b/exercises/conversions/from_str.rs @@ -4,6 +4,8 @@ // Additionally, upon implementing FromStr, you can use the `parse` method // on strings to generate an object of the implementor type. // You can read more about it at https://doc.rust-lang.org/std/str/trait.FromStr.html +// Execute `rustlings hint from_str` or use the `hint` watch subcommand for a hint. + use std::num::ParseIntError; use std::str::FromStr; @@ -37,6 +39,9 @@ enum ParsePersonError { // with something like `"4".parse::()` // 6. If while extracting the name and the age something goes wrong, an error should be returned // If everything goes well, then return a Result of a Person object +// +// As an aside: `Box` implements `From<&'_ str>`. This means that if you want to return a +// string error message, you can do so via just using return `Err("my error message".into())`. impl FromStr for Person { type Err = ParsePersonError; diff --git a/exercises/conversions/mod.rs b/exercises/conversions/mod.rs deleted file mode 100644 index 69f66ae4..00000000 --- a/exercises/conversions/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod as_ref_mut; -mod from_into; -mod from_str; -mod try_from_into; -mod using_as; diff --git a/exercises/conversions/try_from_into.rs b/exercises/conversions/try_from_into.rs index b8ec4455..fa98bc90 100644 --- a/exercises/conversions/try_from_into.rs +++ b/exercises/conversions/try_from_into.rs @@ -3,6 +3,8 @@ // Basically, this is the same as From. The main difference is that this should return a Result type // instead of the target type itself. // You can read more about it at https://doc.rust-lang.org/std/convert/trait.TryFrom.html +// Execute `rustlings hint try_from_into` or use the `hint` watch subcommand for a hint. + use std::convert::{TryFrom, TryInto}; #[derive(Debug, PartialEq)] @@ -54,7 +56,7 @@ impl TryFrom<&[i16]> for Color { } fn main() { - // Use the `from` function + // Use the `try_from` function let c1 = Color::try_from((183, 65, 14)); println!("{:?}", c1); diff --git a/exercises/conversions/using_as.rs b/exercises/conversions/using_as.rs index f3f745ff..8c9b7113 100644 --- a/exercises/conversions/using_as.rs +++ b/exercises/conversions/using_as.rs @@ -4,6 +4,7 @@ // // The goal is to make sure that the division does not fail to compile // and returns the proper type. +// Execute `rustlings hint using_as` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/enums/enums1.rs b/exercises/enums/enums1.rs index a2223d33..511ba740 100644 --- a/exercises/enums/enums1.rs +++ b/exercises/enums/enums1.rs @@ -1,5 +1,5 @@ // enums1.rs -// Make me compile! Execute `rustlings hint enums1` for hints! +// No hints this time! ;) // I AM NOT DONE diff --git a/exercises/enums/enums2.rs b/exercises/enums/enums2.rs index ec32d952..18479f87 100644 --- a/exercises/enums/enums2.rs +++ b/exercises/enums/enums2.rs @@ -1,5 +1,5 @@ // enums2.rs -// Make me compile! Execute `rustlings hint enums2` for hints! +// Execute `rustlings hint enums2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/enums/enums3.rs b/exercises/enums/enums3.rs index 178b40c4..55acf6bc 100644 --- a/exercises/enums/enums3.rs +++ b/exercises/enums/enums3.rs @@ -1,5 +1,6 @@ // enums3.rs // Address all the TODOs to make the tests pass! +// Execute `rustlings hint enums3` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/enums/mod.rs b/exercises/enums/mod.rs deleted file mode 100644 index a23fd6e5..00000000 --- a/exercises/enums/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod enums1; -mod enums2; -mod enums3; diff --git a/exercises/error_handling/errors1.rs b/exercises/error_handling/errors1.rs index c417fb26..bcee9723 100644 --- a/exercises/error_handling/errors1.rs +++ b/exercises/error_handling/errors1.rs @@ -3,16 +3,16 @@ // you pass it an empty string. It'd be nicer if it explained what the problem // was, instead of just sometimes returning `None`. Thankfully, Rust has a similar // construct to `Option` that can be used to express error conditions. Let's use it! -// Execute `rustlings hint errors1` for hints! +// Execute `rustlings hint errors1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE pub fn generate_nametag_text(name: String) -> Option { - if name.len() > 0 { - Some(format!("Hi! My name is {}", name)) - } else { + if name.is_empty() { // Empty names aren't allowed. None + } else { + Some(format!("Hi! My name is {}", name)) } } diff --git a/exercises/error_handling/errors2.rs b/exercises/error_handling/errors2.rs index aad3a93f..1cd8fc66 100644 --- a/exercises/error_handling/errors2.rs +++ b/exercises/error_handling/errors2.rs @@ -14,7 +14,8 @@ // and add. // There are at least two ways to implement this that are both correct-- but -// one is a lot shorter! Execute `rustlings hint errors2` for hints to both ways. +// one is a lot shorter! +// Execute `rustlings hint errors2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/error_handling/errors3.rs b/exercises/error_handling/errors3.rs index 460ac5c4..a2d2d190 100644 --- a/exercises/error_handling/errors3.rs +++ b/exercises/error_handling/errors3.rs @@ -2,7 +2,7 @@ // This is a program that is trying to use a completed version of the // `total_cost` function from the previous exercise. It's not working though! // Why not? What should we do to fix it? -// Execute `rustlings hint errors3` for hints! +// Execute `rustlings hint errors3` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/error_handling/errors4.rs b/exercises/error_handling/errors4.rs index 0685c374..0efe8ccd 100644 --- a/exercises/error_handling/errors4.rs +++ b/exercises/error_handling/errors4.rs @@ -1,5 +1,5 @@ // errors4.rs -// Make this test pass! Execute `rustlings hint errors4` for hints :) +// Execute `rustlings hint errors4` or use the `hint` watch subcommand for a hint. // I AM NOT DONE @@ -14,6 +14,7 @@ enum CreationError { impl PositiveNonzeroInteger { fn new(value: i64) -> Result { + // Hmm...? Why is this only returning an Ok value? Ok(PositiveNonzeroInteger(value as u64)) } } diff --git a/exercises/error_handling/errors5.rs b/exercises/error_handling/errors5.rs index 365a8691..67411c58 100644 --- a/exercises/error_handling/errors5.rs +++ b/exercises/error_handling/errors5.rs @@ -1,8 +1,18 @@ // errors5.rs -// This program uses a completed version of the code from errors4. -// It won't compile right now! Why? -// Execute `rustlings hint errors5` for hints! +// This program uses an altered version of the code from errors4. + +// This exercise uses some concepts that we won't get to until later in the course, like `Box` and the +// `From` trait. It's not important to understand them in detail right now, but you can read ahead if you like. + +// In short, this particular use case for boxes is for when you want to own a value and you care only that it is a +// type which implements a particular trait. To do so, The Box is declared as of type Box where Trait is the trait +// the compiler looks for on any value used in that context. For this exercise, that context is the potential errors +// which can be returned in a Result. + +// What can we use to describe both errors? In other words, is there a trait which both errors implement? + +// Execute `rustlings hint errors5` or use the `hint` watch subcommand for a hint. // I AM NOT DONE @@ -11,7 +21,7 @@ use std::fmt; use std::num::ParseIntError; // TODO: update the return type of `main()` to make this compile. -fn main() -> Result<(), ParseIntError> { +fn main() -> Result<(), Box> { let pretend_user_input = "42"; let x: i64 = pretend_user_input.parse()?; println!("output={:?}", PositiveNonzeroInteger::new(x)?); diff --git a/exercises/error_handling/errors6.rs b/exercises/error_handling/errors6.rs index 847a049a..1306fb03 100644 --- a/exercises/error_handling/errors6.rs +++ b/exercises/error_handling/errors6.rs @@ -6,7 +6,7 @@ // we define a custom error type to make it possible for callers to decide // what to do next when our function returns an error. -// Make these tests pass! Execute `rustlings hint errors6` for hints :) +// Execute `rustlings hint errors6` or use the `hint` watch subcommand for a hint. // I AM NOT DONE @@ -20,7 +20,11 @@ enum ParsePosNonzeroError { } impl ParsePosNonzeroError { + fn from_creation(err: CreationError) -> ParsePosNonzeroError { + ParsePosNonzeroError::Creation(err) + } // TODO: add another error conversion function here. + // fn from_parseint... } fn parse_pos_nonzero(s: &str) diff --git a/exercises/error_handling/mod.rs b/exercises/error_handling/mod.rs deleted file mode 100644 index 539fa23d..00000000 --- a/exercises/error_handling/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -mod errors1; -mod errors2; -mod errors3; -mod errors4; -mod errors5; -mod errors6; diff --git a/exercises/functions/README.md b/exercises/functions/README.md index 66547bd4..6662d0da 100644 --- a/exercises/functions/README.md +++ b/exercises/functions/README.md @@ -1,6 +1,7 @@ # Functions -Here, you'll learn how to write functions and how Rust's compiler can trace things way back. +Here, you'll learn how to write functions and how the Rust compiler can help you debug errors even +in more complex code. ## Further information diff --git a/exercises/functions/functions1.rs b/exercises/functions/functions1.rs index 31125278..03d8af70 100644 --- a/exercises/functions/functions1.rs +++ b/exercises/functions/functions1.rs @@ -1,5 +1,5 @@ // functions1.rs -// Make me compile! Execute `rustlings hint functions1` for hints :) +// Execute `rustlings hint functions1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/functions/functions2.rs b/exercises/functions/functions2.rs index 5721a172..7d40a578 100644 --- a/exercises/functions/functions2.rs +++ b/exercises/functions/functions2.rs @@ -1,5 +1,5 @@ // functions2.rs -// Make me compile! Execute `rustlings hint functions2` for hints :) +// Execute `rustlings hint functions2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/functions/functions3.rs b/exercises/functions/functions3.rs index ed5f839f..3b9e585b 100644 --- a/exercises/functions/functions3.rs +++ b/exercises/functions/functions3.rs @@ -1,5 +1,5 @@ // functions3.rs -// Make me compile! Execute `rustlings hint functions3` for hints :) +// Execute `rustlings hint functions3` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/functions/functions4.rs b/exercises/functions/functions4.rs index 58637e4c..65d5be4f 100644 --- a/exercises/functions/functions4.rs +++ b/exercises/functions/functions4.rs @@ -1,8 +1,11 @@ // functions4.rs -// Make me compile! Execute `rustlings hint functions4` for hints :) +// Execute `rustlings hint functions4` or use the `hint` watch subcommand for a hint. // This store is having a sale where if the price is an even number, you get // 10 Rustbucks off, but if it's an odd number, it's 3 Rustbucks off. +// (Don't worry about the function bodies themselves, we're only interested +// in the signatures for now. If anything, this is a good way to peek ahead +// to future exercises!) // I AM NOT DONE diff --git a/exercises/functions/functions5.rs b/exercises/functions/functions5.rs index d22aa6c8..5d762961 100644 --- a/exercises/functions/functions5.rs +++ b/exercises/functions/functions5.rs @@ -1,11 +1,11 @@ // functions5.rs -// Make me compile! Execute `rustlings hint functions5` for hints :) +// Execute `rustlings hint functions5` or use the `hint` watch subcommand for a hint. // I AM NOT DONE fn main() { let answer = square(3); - println!("The answer is {}", answer); + println!("The square of 3 is {}", answer); } fn square(num: i32) -> i32 { diff --git a/exercises/functions/mod.rs b/exercises/functions/mod.rs deleted file mode 100644 index 445b6f58..00000000 --- a/exercises/functions/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod functions1; -mod functions2; -mod functions3; -mod functions4; -mod functions5; diff --git a/exercises/generics/generics1.rs b/exercises/generics/generics1.rs index f93e64a0..4c34ae47 100644 --- a/exercises/generics/generics1.rs +++ b/exercises/generics/generics1.rs @@ -1,7 +1,7 @@ // This shopping list program isn't compiling! // Use your knowledge of generics to fix it. -// Execute `rustlings hint generics1` for hints! +// Execute `rustlings hint generics1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/generics/generics2.rs b/exercises/generics/generics2.rs index 1501529c..aedbd55c 100644 --- a/exercises/generics/generics2.rs +++ b/exercises/generics/generics2.rs @@ -1,7 +1,7 @@ // This powerful wrapper provides the ability to store a positive integer value. // Rewrite it using generics so that it supports wrapping ANY type. -// Execute `rustlings hint generics2` for hints! +// Execute `rustlings hint generics2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/generics/generics3.rs b/exercises/generics/generics3.rs deleted file mode 100644 index 64dd9bc1..00000000 --- a/exercises/generics/generics3.rs +++ /dev/null @@ -1,58 +0,0 @@ -// An imaginary magical school has a new report card generation system written in Rust! -// Currently the system only supports creating report cards where the student's grade -// is represented numerically (e.g. 1.0 -> 5.5). -// However, the school also issues alphabetical grades (A+ -> F-) and needs -// to be able to print both types of report card! - -// Make the necessary code changes in the struct ReportCard and the impl block -// to support alphabetical report cards. Change the Grade in the second test to "A+" -// to show that your changes allow alphabetical grades. - -// Execute 'rustlings hint generics3' for hints! - -// I AM NOT DONE - -pub struct ReportCard { - pub grade: f32, - pub student_name: String, - pub student_age: u8, -} - -impl ReportCard { - pub fn print(&self) -> String { - format!("{} ({}) - achieved a grade of {}", - &self.student_name, &self.student_age, &self.grade) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn generate_numeric_report_card() { - let report_card = ReportCard { - grade: 2.1, - student_name: "Tom Wriggle".to_string(), - student_age: 12, - }; - assert_eq!( - report_card.print(), - "Tom Wriggle (12) - achieved a grade of 2.1" - ); - } - - #[test] - fn generate_alphabetic_report_card() { - // TODO: Make sure to change the grade here after you finish the exercise. - let report_card = ReportCard { - grade: 2.1, - student_name: "Gary Plotter".to_string(), - student_age: 11, - }; - assert_eq!( - report_card.print(), - "Gary Plotter (11) - achieved a grade of A+" - ); - } -} diff --git a/exercises/generics/mod.rs b/exercises/generics/mod.rs deleted file mode 100644 index 5b93555c..00000000 --- a/exercises/generics/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod generics1; -mod generics2; -mod generics3; diff --git a/exercises/hashmaps/README.md b/exercises/hashmaps/README.md new file mode 100644 index 00000000..30471cf9 --- /dev/null +++ b/exercises/hashmaps/README.md @@ -0,0 +1,11 @@ +# Hashmaps +A *hash map* allows you to associate a value with a particular key. +You may also know this by the names [*unordered map* in C++](https://en.cppreference.com/w/cpp/container/unordered_map), +[*dictionary* in Python](https://docs.python.org/3/tutorial/datastructures.html#dictionaries) or an *associative array* in other languages. + +This is the other data structure that we've been talking about before, when +talking about Vecs. + +## Further information + +- [Storing Keys with Associated Values in Hash Maps](https://doc.rust-lang.org/book/ch08-03-hash-maps.html) diff --git a/exercises/collections/hashmap1.rs b/exercises/hashmaps/hashmaps1.rs similarity index 91% rename from exercises/collections/hashmap1.rs rename to exercises/hashmaps/hashmaps1.rs index 64b5a7f3..fd8dd2f8 100644 --- a/exercises/collections/hashmap1.rs +++ b/exercises/hashmaps/hashmaps1.rs @@ -1,4 +1,4 @@ -// hashmap1.rs +// hashmaps1.rs // A basket of fruits in the form of a hash map needs to be defined. // The key represents the name of the fruit and the value represents // how many of that particular fruit is in the basket. You have to put @@ -8,8 +8,7 @@ // // Make me compile and pass the tests! // -// Execute the command `rustlings hint hashmap1` if you need -// hints. +// Execute `rustlings hint hashmaps1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/collections/hashmap2.rs b/exercises/hashmaps/hashmaps2.rs similarity index 95% rename from exercises/collections/hashmap2.rs rename to exercises/hashmaps/hashmaps2.rs index 0abe19ab..454b3e1d 100644 --- a/exercises/collections/hashmap2.rs +++ b/exercises/hashmaps/hashmaps2.rs @@ -1,4 +1,4 @@ -// hashmap2.rs +// hashmaps2.rs // A basket of fruits in the form of a hash map is given. The key // represents the name of the fruit and the value represents how many @@ -9,8 +9,7 @@ // // Make me pass the tests! // -// Execute the command `rustlings hint hashmap2` if you need -// hints. +// Execute `rustlings hint hashmaps2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/hashmaps/hashmaps3.rs b/exercises/hashmaps/hashmaps3.rs new file mode 100644 index 00000000..18dd44c9 --- /dev/null +++ b/exercises/hashmaps/hashmaps3.rs @@ -0,0 +1,87 @@ +// hashmaps3.rs + +// A list of scores (one per line) of a soccer match is given. Each line +// is of the form : +// ,,, +// Example: England,France,4,2 (England scored 4 goals, France 2). + +// You have to build a scores table containing the name of the team, goals +// the team scored, and goals the team conceded. One approach to build +// the scores table is to use a Hashmap. The solution is partially +// written to use a Hashmap, complete it to pass the test. + +// Make me pass the tests! + +// Execute `rustlings hint hashmaps3` or use the `hint` watch subcommand for a hint. + +// I AM NOT DONE + +use std::collections::HashMap; + +// A structure to store team name and its goal details. +struct Team { + name: String, + goals_scored: u8, + goals_conceded: u8, +} + +fn build_scores_table(results: String) -> HashMap { + // The name of the team is the key and its associated struct is the value. + let mut scores: HashMap = HashMap::new(); + + for r in results.lines() { + let v: Vec<&str> = r.split(',').collect(); + let team_1_name = v[0].to_string(); + let team_1_score: u8 = v[2].parse().unwrap(); + let team_2_name = v[1].to_string(); + let team_2_score: u8 = v[3].parse().unwrap(); + // TODO: Populate the scores table with details extracted from the + // current line. Keep in mind that goals scored by team_1 + // will be number of goals conceded from team_2, and similarly + // goals scored by team_2 will be the number of goals conceded by + // team_1. + } + scores +} + +#[cfg(test)] +mod tests { + use super::*; + + fn get_results() -> String { + let results = "".to_string() + + "England,France,4,2\n" + + "France,Italy,3,1\n" + + "Poland,Spain,2,0\n" + + "Germany,England,2,1\n"; + results + } + + #[test] + fn build_scores() { + let scores = build_scores_table(get_results()); + + let mut keys: Vec<&String> = scores.keys().collect(); + keys.sort(); + assert_eq!( + keys, + vec!["England", "France", "Germany", "Italy", "Poland", "Spain"] + ); + } + + #[test] + fn validate_team_score_1() { + let scores = build_scores_table(get_results()); + let team = scores.get("England").unwrap(); + assert_eq!(team.goals_scored, 5); + assert_eq!(team.goals_conceded, 4); + } + + #[test] + fn validate_team_score_2() { + let scores = build_scores_table(get_results()); + let team = scores.get("Spain").unwrap(); + assert_eq!(team.goals_scored, 0); + assert_eq!(team.goals_conceded, 2); + } +} diff --git a/exercises/if/README.md b/exercises/if/README.md index 528d9886..b52c3922 100644 --- a/exercises/if/README.md +++ b/exercises/if/README.md @@ -1,6 +1,6 @@ # If -`if`, the most basic type of control flow, is what you'll learn here. +`if`, the most basic (but still surprisingly versatile!) type of control flow, is what you'll learn here. ## Further information diff --git a/exercises/if/if1.rs b/exercises/if/if1.rs index 90867545..587e03f8 100644 --- a/exercises/if/if1.rs +++ b/exercises/if/if1.rs @@ -1,4 +1,5 @@ // if1.rs +// Execute `rustlings hint if1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE @@ -7,7 +8,6 @@ pub fn bigger(a: i32, b: i32) -> i32 { // Do not use: // - another function call // - additional variables - // Execute `rustlings hint if1` for hints } // Don't mind this for now :) diff --git a/exercises/if/if2.rs b/exercises/if/if2.rs index 80effbdf..effddbb6 100644 --- a/exercises/if/if2.rs +++ b/exercises/if/if2.rs @@ -2,11 +2,11 @@ // Step 1: Make me compile! // Step 2: Get the bar_for_fuzz and default_to_baz tests passing! -// Execute the command `rustlings hint if2` if you want a hint :) +// Execute `rustlings hint if2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE -pub fn fizz_if_foo(fizzish: &str) -> &str { +pub fn foo_if_fizz(fizzish: &str) -> &str { if fizzish == "fizz" { "foo" } else { @@ -21,16 +21,16 @@ mod tests { #[test] fn foo_for_fizz() { - assert_eq!(fizz_if_foo("fizz"), "foo") + assert_eq!(foo_if_fizz("fizz"), "foo") } #[test] fn bar_for_fuzz() { - assert_eq!(fizz_if_foo("fuzz"), "bar") + assert_eq!(foo_if_fizz("fuzz"), "bar") } #[test] fn default_to_baz() { - assert_eq!(fizz_if_foo("literally anything"), "baz") + assert_eq!(foo_if_fizz("literally anything"), "baz") } } diff --git a/exercises/if/mod.rs b/exercises/if/mod.rs deleted file mode 100644 index c5d02445..00000000 --- a/exercises/if/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod if1; -mod if2; diff --git a/exercises/intro/intro1.rs b/exercises/intro/intro1.rs index 1c4582de..45c5acba 100644 --- a/exercises/intro/intro1.rs +++ b/exercises/intro/intro1.rs @@ -3,7 +3,11 @@ // We sometimes encourage you to keep trying things on a given exercise, even // after you already figured it out. If you got everything working and feel // ready for the next exercise, remove the `I AM NOT DONE` comment below. -// Execute the command `rustlings hint intro1` for a hint. +// Execute `rustlings hint intro1` or use the `hint` watch subcommand for a hint. +// +// If you're running this using `rustlings watch`: The exercise file will be reloaded +// when you change one of the lines below! Try adding a `println!` line, or try changing +// what it outputs in your terminal. Try removing a semicolon and see what happens! // I AM NOT DONE @@ -20,4 +24,7 @@ fn main() { println!("This exercise compiles successfully. The remaining exercises contain a compiler"); println!("or logic error. The central concept behind Rustlings is to fix these errors and"); println!("solve the exercises. Good luck!"); + println!(); + println!("The source for this exercise is in `exercises/intro/intro1.rs`. Have a look!"); + println!("Going forward, the source of the exercises will always be in the success/failure output."); } diff --git a/exercises/intro/intro2.rs b/exercises/intro/intro2.rs index 97a073f0..efc1af20 100644 --- a/exercises/intro/intro2.rs +++ b/exercises/intro/intro2.rs @@ -1,6 +1,6 @@ // intro2.rs // Make the code print a greeting to the world. -// Execute `rustlings hint intro2` for a hint. +// Execute `rustlings hint intro2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/intro/mod.rs b/exercises/intro/mod.rs deleted file mode 100644 index 445c47ab..00000000 --- a/exercises/intro/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod intro1; -mod intro2; diff --git a/exercises/lifetimes/README.md b/exercises/lifetimes/README.md new file mode 100644 index 00000000..72befb3e --- /dev/null +++ b/exercises/lifetimes/README.md @@ -0,0 +1,17 @@ +# Lifetimes + +Lifetimes tell the compiler how to check whether references live long +enough to be valid in any given situation. For example lifetimes say +"make sure parameter 'a' lives as long as parameter 'b' so that the return +value is valid". + +They are only necessary on borrows, i.e. references, +since copied parameters or moves are owned in their scope and cannot +be referenced outside. Lifetimes mean that calling code of e.g. functions +can be checked to make sure their arguments are valid. Lifetimes are +restrictive of their callers. + +## Further information + +- [Validating References with Lifetimes](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html) +- [Lifetimes (in Rust By Example)](https://doc.rust-lang.org/stable/rust-by-example/scope/lifetime.html) diff --git a/exercises/lifetimes/lifetimes1.rs b/exercises/lifetimes/lifetimes1.rs new file mode 100644 index 00000000..58e995c6 --- /dev/null +++ b/exercises/lifetimes/lifetimes1.rs @@ -0,0 +1,26 @@ +// lifetimes1.rs +// +// The Rust compiler needs to know how to check whether supplied references are +// valid, so that it can let the programmer know if a reference is at risk +// of going out of scope before it is used. Remember, references are borrows +// and do not own their own data. What if their owner goes out of scope? +// +// Execute `rustlings hint lifetimes1` or use the `hint` watch subcommand for a hint. + +// I AM NOT DONE + +fn longest(x: &str, y: &str) -> &str { + if x.len() > y.len() { + x + } else { + y + } +} + +fn main() { + let string1 = String::from("abcd"); + let string2 = "xyz"; + + let result = longest(string1.as_str(), string2); + println!("The longest string is {}", result); +} diff --git a/exercises/lifetimes/lifetimes2.rs b/exercises/lifetimes/lifetimes2.rs new file mode 100644 index 00000000..c73a28ad --- /dev/null +++ b/exercises/lifetimes/lifetimes2.rs @@ -0,0 +1,27 @@ +// lifetimes2.rs +// +// So if the compiler is just validating the references passed +// to the annotated parameters and the return type, what do +// we need to change? +// +// Execute `rustlings hint lifetimes2` or use the `hint` watch subcommand for a hint. + +// I AM NOT DONE + +fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { + if x.len() > y.len() { + x + } else { + y + } +} + +fn main() { + let string1 = String::from("long string is long"); + let result; + { + let string2 = String::from("xyz"); + result = longest(string1.as_str(), string2.as_str()); + } + println!("The longest string is {}", result); +} diff --git a/exercises/lifetimes/lifetimes3.rs b/exercises/lifetimes/lifetimes3.rs new file mode 100644 index 00000000..ea483708 --- /dev/null +++ b/exercises/lifetimes/lifetimes3.rs @@ -0,0 +1,20 @@ +// lifetimes3.rs +// +// Lifetimes are also needed when structs hold references. +// +// Execute `rustlings hint lifetimes3` or use the `hint` watch subcommand for a hint. + +// I AM NOT DONE + +struct Book { + author: &str, + title: &str, +} + +fn main() { + let name = String::from("Jill Smith"); + let title = String::from("Fish Flying"); + let book = Book { author: &name, title: &title }; + + println!("{} by {}", book.title, book.author); +} diff --git a/exercises/macros/macros1.rs b/exercises/macros/macros1.rs index ed0dac85..634d0a70 100644 --- a/exercises/macros/macros1.rs +++ b/exercises/macros/macros1.rs @@ -1,5 +1,5 @@ // macros1.rs -// Make me compile! Execute `rustlings hint macros1` for hints :) +// Execute `rustlings hint macros1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/macros/macros2.rs b/exercises/macros/macros2.rs index d0be1236..f6092cab 100644 --- a/exercises/macros/macros2.rs +++ b/exercises/macros/macros2.rs @@ -1,5 +1,5 @@ // macros2.rs -// Make me compile! Execute `rustlings hint macros2` for hints :) +// Execute `rustlings hint macros2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/macros/macros3.rs b/exercises/macros/macros3.rs index 93a43113..106f1c6d 100644 --- a/exercises/macros/macros3.rs +++ b/exercises/macros/macros3.rs @@ -1,6 +1,6 @@ // macros3.rs // Make me compile, without taking the macro out of the module! -// Execute `rustlings hint macros3` for hints :) +// Execute `rustlings hint macros3` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/macros/macros4.rs b/exercises/macros/macros4.rs index 3a748078..c1fc5e8b 100644 --- a/exercises/macros/macros4.rs +++ b/exercises/macros/macros4.rs @@ -1,5 +1,5 @@ // macros4.rs -// Make me compile! Execute `rustlings hint macros4` for hints :) +// Execute `rustlings hint macros4` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/macros/mod.rs b/exercises/macros/mod.rs deleted file mode 100644 index 9f65acf5..00000000 --- a/exercises/macros/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod macros1; -mod macros2; -mod macros3; -mod macros4; diff --git a/exercises/mod.rs b/exercises/mod.rs deleted file mode 100644 index 6a143b56..00000000 --- a/exercises/mod.rs +++ /dev/null @@ -1,26 +0,0 @@ -mod advanced_errors; -mod clippy; -mod collections; -mod conversions; -mod enums; -mod error_handling; -mod functions; -mod generics; -mod r#if; -mod intro; -mod macros; -mod modules; -mod move_semantics; -mod option; -mod primitive_types; -mod quiz1; -mod quiz2; -mod quiz3; -mod quiz4; -mod standard_library_types; -mod strings; -mod structs; -mod tests; -mod threads; -mod traits; -mod variables; diff --git a/exercises/modules/mod.rs b/exercises/modules/mod.rs deleted file mode 100644 index 35f96af5..00000000 --- a/exercises/modules/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod modules1; -mod modules2; -mod modules3; diff --git a/exercises/modules/modules1.rs b/exercises/modules/modules1.rs index 1a2bd0dd..8dd0e402 100644 --- a/exercises/modules/modules1.rs +++ b/exercises/modules/modules1.rs @@ -1,5 +1,5 @@ // modules1.rs -// Make me compile! Execute `rustlings hint modules1` for hints :) +// Execute `rustlings hint modules1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/modules/modules2.rs b/exercises/modules/modules2.rs index 87f0c458..c30a3897 100644 --- a/exercises/modules/modules2.rs +++ b/exercises/modules/modules2.rs @@ -1,12 +1,11 @@ // modules2.rs // You can bring module paths into scopes and provide new names for them with the // 'use' and 'as' keywords. Fix these 'use' statements to make the code compile. -// Make me compile! Execute `rustlings hint modules2` for hints :) +// Execute `rustlings hint modules2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE mod delicious_snacks { - // TODO: Fix these use statements use self::fruits::PEAR as ??? use self::veggies::CUCUMBER as ??? diff --git a/exercises/modules/modules3.rs b/exercises/modules/modules3.rs index 8eed77df..35e07990 100644 --- a/exercises/modules/modules3.rs +++ b/exercises/modules/modules3.rs @@ -3,7 +3,7 @@ // and especially from the Rust standard library into your scope. // Bring SystemTime and UNIX_EPOCH // from the std::time module. Bonus style points if you can do it with one line! -// Make me compile! Execute `rustlings hint modules3` for hints :) +// Execute `rustlings hint modules3` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/move_semantics/mod.rs b/exercises/move_semantics/mod.rs deleted file mode 100644 index e8eecf0a..00000000 --- a/exercises/move_semantics/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -mod move_semantics1; -mod move_semantics2; -mod move_semantics3; -mod move_semantics4; -mod move_semantics5; -mod move_semantics6; diff --git a/exercises/move_semantics/move_semantics1.rs b/exercises/move_semantics/move_semantics1.rs index e2f5876d..aac6dfc3 100644 --- a/exercises/move_semantics/move_semantics1.rs +++ b/exercises/move_semantics/move_semantics1.rs @@ -1,5 +1,5 @@ // move_semantics1.rs -// Make me compile! Execute `rustlings hint move_semantics1` for hints :) +// Execute `rustlings hint move_semantics1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/move_semantics/move_semantics2.rs b/exercises/move_semantics/move_semantics2.rs index 888dc529..64870850 100644 --- a/exercises/move_semantics/move_semantics2.rs +++ b/exercises/move_semantics/move_semantics2.rs @@ -1,6 +1,6 @@ // move_semantics2.rs // Make me compile without changing line 13 or moving line 10! -// Execute `rustlings hint move_semantics2` for hints :) +// Execute `rustlings hint move_semantics2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/move_semantics/move_semantics3.rs b/exercises/move_semantics/move_semantics3.rs index 43fef74f..eaa30e33 100644 --- a/exercises/move_semantics/move_semantics3.rs +++ b/exercises/move_semantics/move_semantics3.rs @@ -1,7 +1,7 @@ // move_semantics3.rs // Make me compile without adding new lines-- just changing existing lines! // (no lines with multiple semicolons necessary!) -// Execute `rustlings hint move_semantics3` for hints :) +// Execute `rustlings hint move_semantics3` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/move_semantics/move_semantics4.rs b/exercises/move_semantics/move_semantics4.rs index 2a23c710..99834ec3 100644 --- a/exercises/move_semantics/move_semantics4.rs +++ b/exercises/move_semantics/move_semantics4.rs @@ -1,8 +1,8 @@ // move_semantics4.rs -// Refactor this code so that instead of having `vec0` and creating the vector -// in `fn main`, we create it within `fn fill_vec` and transfer the -// freshly created vector from fill_vec to its caller. -// Execute `rustlings hint move_semantics4` for hints! +// Refactor this code so that instead of passing `vec0` into the `fill_vec` function, +// the Vector gets created in the function itself and passed back to the main +// function. +// Execute `rustlings hint move_semantics4` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/move_semantics/move_semantics5.rs b/exercises/move_semantics/move_semantics5.rs index c4704f9e..36eae127 100644 --- a/exercises/move_semantics/move_semantics5.rs +++ b/exercises/move_semantics/move_semantics5.rs @@ -1,7 +1,7 @@ // move_semantics5.rs // Make me compile only by reordering the lines in `main()`, but without // adding, changing or removing any of them. -// Execute `rustlings hint move_semantics5` for hints :) +// Execute `rustlings hint move_semantics5` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/move_semantics/move_semantics6.rs b/exercises/move_semantics/move_semantics6.rs index 457e7ae7..eb52a848 100644 --- a/exercises/move_semantics/move_semantics6.rs +++ b/exercises/move_semantics/move_semantics6.rs @@ -1,6 +1,6 @@ // move_semantics6.rs -// Make me compile! `rustlings hint move_semantics6` for hints -// You can't change anything except adding or removing references +// Execute `rustlings hint move_semantics6` or use the `hint` watch subcommand for a hint. +// You can't change anything except adding or removing references. // I AM NOT DONE diff --git a/exercises/option/mod.rs b/exercises/option/mod.rs deleted file mode 100644 index b3cdb13d..00000000 --- a/exercises/option/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod option1; -mod option2; -mod option3; diff --git a/exercises/option/option1.rs b/exercises/option/option1.rs deleted file mode 100644 index 17cf4f60..00000000 --- a/exercises/option/option1.rs +++ /dev/null @@ -1,23 +0,0 @@ -// option1.rs -// Make me compile! Execute `rustlings hint option1` for hints - -// I AM NOT DONE - -// you can modify anything EXCEPT for this function's signature -fn print_number(maybe_number: Option) { - println!("printing: {}", maybe_number.unwrap()); -} - -fn main() { - print_number(13); - print_number(99); - - let mut numbers: [Option; 5]; - for iter in 0..5 { - let number_to_add: u16 = { - ((iter * 1235) + 2) / (4 * 16) - }; - - numbers[iter as usize] = number_to_add; - } -} diff --git a/exercises/option/README.md b/exercises/options/README.md similarity index 99% rename from exercises/option/README.md rename to exercises/options/README.md index 89c00289..6140a167 100644 --- a/exercises/option/README.md +++ b/exercises/options/README.md @@ -1,4 +1,4 @@ -# Option +# Options Type Option represents an optional value: every Option is either Some and contains a value, or None, and does not. Option types are very common in Rust code, as they have a number of uses: diff --git a/exercises/options/options1.rs b/exercises/options/options1.rs new file mode 100644 index 00000000..038fb48e --- /dev/null +++ b/exercises/options/options1.rs @@ -0,0 +1,37 @@ +// options1.rs +// Execute `rustlings hint options1` or use the `hint` watch subcommand for a hint. + +// I AM NOT DONE + +// you can modify anything EXCEPT for this function's signature +fn print_number(maybe_number: Option) { + println!("printing: {}", maybe_number.unwrap()); +} + +// This function returns how much icecream there is left in the fridge. +// If it's before 10PM, there's 5 pieces left. At 10PM, someone eats them +// all, so there'll be no more left :( +// TODO: Return an Option! +fn maybe_icecream(time_of_day: u16) -> Option { + // We use the 24-hour system here, so 10PM is a value of 22 + ??? +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn check_icecream() { + assert_eq!(maybe_icecream(10), Some(5)); + assert_eq!(maybe_icecream(23), None); + assert_eq!(maybe_icecream(22), None); + } + + #[test] + fn raw_value() { + // TODO: Fix this test. How do you get at the value contained in the Option? + let icecreams = maybe_icecream(12); + assert_eq!(icecreams, 5); + } +} diff --git a/exercises/option/option2.rs b/exercises/options/options2.rs similarity index 87% rename from exercises/option/option2.rs rename to exercises/options/options2.rs index c6b83ece..75b66a37 100644 --- a/exercises/option/option2.rs +++ b/exercises/options/options2.rs @@ -1,5 +1,5 @@ -// option2.rs -// Make me compile! Execute `rustlings hint option2` for hints +// options2.rs +// Execute `rustlings hint options2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/option/option3.rs b/exercises/options/options3.rs similarity index 75% rename from exercises/option/option3.rs rename to exercises/options/options3.rs index 045d2acb..3f995c52 100644 --- a/exercises/option/option3.rs +++ b/exercises/options/options3.rs @@ -1,5 +1,5 @@ -// option3.rs -// Make me compile! Execute `rustlings hint option3` for hints +// options3.rs +// Execute `rustlings hint options3` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/primitive_types/mod.rs b/exercises/primitive_types/mod.rs deleted file mode 100644 index 23355239..00000000 --- a/exercises/primitive_types/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -mod primitive_types1; -mod primitive_types2; -mod primitive_types3; -mod primitive_types4; -mod primitive_types5; -mod primitive_types6; diff --git a/exercises/primitive_types/primitive_types2.rs b/exercises/primitive_types/primitive_types2.rs index 6576a4d5..8730baab 100644 --- a/exercises/primitive_types/primitive_types2.rs +++ b/exercises/primitive_types/primitive_types2.rs @@ -7,6 +7,8 @@ fn main() { // Characters (`char`) + // Note the _single_ quotes, these are different from the double quotes + // you've been seeing around. let my_first_initial = 'C'; if my_first_initial.is_alphabetic() { println!("Alphabetical!"); diff --git a/exercises/primitive_types/primitive_types3.rs b/exercises/primitive_types/primitive_types3.rs index aaa518be..fa7d019a 100644 --- a/exercises/primitive_types/primitive_types3.rs +++ b/exercises/primitive_types/primitive_types3.rs @@ -1,6 +1,6 @@ // primitive_types3.rs // Create an array with at least 100 elements in it where the ??? is. -// Execute `rustlings hint primitive_types3` for hints! +// Execute `rustlings hint primitive_types3` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/primitive_types/primitive_types4.rs b/exercises/primitive_types/primitive_types4.rs index 10b553e9..71fa243c 100644 --- a/exercises/primitive_types/primitive_types4.rs +++ b/exercises/primitive_types/primitive_types4.rs @@ -1,6 +1,6 @@ // primitive_types4.rs // Get a slice out of Array a where the ??? is so that the test passes. -// Execute `rustlings hint primitive_types4` for hints!! +// Execute `rustlings hint primitive_types4` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/primitive_types/primitive_types5.rs b/exercises/primitive_types/primitive_types5.rs index 680d8d23..4fd9141f 100644 --- a/exercises/primitive_types/primitive_types5.rs +++ b/exercises/primitive_types/primitive_types5.rs @@ -1,6 +1,6 @@ // primitive_types5.rs // Destructure the `cat` tuple so that the println will work. -// Execute `rustlings hint primitive_types5` for hints! +// Execute `rustlings hint primitive_types5` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/primitive_types/primitive_types6.rs b/exercises/primitive_types/primitive_types6.rs index b8c9b82b..ddf8b423 100644 --- a/exercises/primitive_types/primitive_types6.rs +++ b/exercises/primitive_types/primitive_types6.rs @@ -1,7 +1,7 @@ // primitive_types6.rs // Use a tuple index to access the second element of `numbers`. // You can put the expression for the second element where ??? is so that the test passes. -// Execute `rustlings hint primitive_types6` for hints! +// Execute `rustlings hint primitive_types6` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/quiz1.rs b/exercises/quiz1.rs index 7bd3f589..8d05b110 100644 --- a/exercises/quiz1.rs +++ b/exercises/quiz1.rs @@ -11,14 +11,14 @@ // I AM NOT DONE // Put your function here! -// fn calculate_apple_price { +// fn calculate_price_of_apples { // Don't modify this function! #[test] fn verify_test() { - let price1 = calculate_apple_price(35); - let price2 = calculate_apple_price(40); - let price3 = calculate_apple_price(65); + let price1 = calculate_price_of_apples(35); + let price2 = calculate_price_of_apples(40); + let price3 = calculate_price_of_apples(65); assert_eq!(70, price1); assert_eq!(80, price2); diff --git a/exercises/quiz2.rs b/exercises/quiz2.rs index de0dce95..f7437fd2 100644 --- a/exercises/quiz2.rs +++ b/exercises/quiz2.rs @@ -1,30 +1,62 @@ // quiz2.rs // This is a quiz for the following sections: // - Strings +// - Vecs +// - Move semantics +// - Modules +// - Enums -// Ok, here are a bunch of values-- some are `String`s, some are `&str`s. Your -// task is to call one of these two functions on each value depending on what -// you think each value is. That is, add either `string_slice` or `string` -// before the parentheses on each line. If you're right, it will compile! +// Let's build a little machine in form of a function. +// As input, we're going to give a list of strings and commands. These commands +// determine what action is going to be applied to the string. It can either be: +// - Uppercase the string +// - Trim the string +// - Append "bar" to the string a specified amount of times +// The exact form of this will be: +// - The input is going to be a Vector of a 2-length tuple, +// the first element is the string, the second one is the command. +// - The output element is going to be a Vector of strings. +// Execute `rustlings hint quiz2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE -fn string_slice(arg: &str) { - println!("{}", arg); -} -fn string(arg: String) { - println!("{}", arg); +pub enum Command { + Uppercase, + Trim, + Append(usize), } -fn main() { - ???("blue"); - ???("red".to_string()); - ???(String::from("hi")); - ???("rust is fun!".to_owned()); - ???("nice weather".into()); - ???(format!("Interpolation {}", "Station")); - ???(&String::from("abc")[0..1]); - ???(" hello there ".trim()); - ???("Happy Monday!".to_string().replace("Mon", "Tues")); - ???("mY sHiFt KeY iS sTiCkY".to_lowercase()); +mod my_module { + use super::Command; + + // TODO: Complete the function signature! + pub fn transformer(input: ???) -> ??? { + // TODO: Complete the output declaration! + let mut output: ??? = vec![]; + for (string, command) in input.iter() { + // TODO: Complete the function body. You can do it! + } + output + } +} + +#[cfg(test)] +mod tests { + // TODO: What to we have to import to have `transformer` in scope? + use ???; + use super::Command; + + #[test] + fn it_works() { + let output = transformer(vec![ + ("hello".into(), Command::Uppercase), + (" all roads lead to rome! ".into(), Command::Trim), + ("foo".into(), Command::Append(1)), + ("bar".into(), Command::Append(5)), + ]); + assert_eq!(output[0], "HELLO"); + assert_eq!(output[1], "all roads lead to rome!"); + assert_eq!(output[2], "foobar"); + assert_eq!(output[3], "barbarbarbarbarbar"); + } } diff --git a/exercises/quiz3.rs b/exercises/quiz3.rs index fae0eedb..15dc4699 100644 --- a/exercises/quiz3.rs +++ b/exercises/quiz3.rs @@ -1,16 +1,32 @@ // quiz3.rs -// This is a quiz for the following sections: -// - Tests +// This quiz tests: +// - Generics +// - Traits +// An imaginary magical school has a new report card generation system written in Rust! +// Currently the system only supports creating report cards where the student's grade +// is represented numerically (e.g. 1.0 -> 5.5). +// However, the school also issues alphabetical grades (A+ -> F-) and needs +// to be able to print both types of report card! -// This quiz isn't testing our function -- make it do that in such a way that -// the test passes. Then write a second test that tests that we get the result -// we expect to get when we call `times_two` with a negative number. -// No hints, you can do this :) +// Make the necessary code changes in the struct ReportCard and the impl block +// to support alphabetical report cards. Change the Grade in the second test to "A+" +// to show that your changes allow alphabetical grades. + +// Execute `rustlings hint quiz3` or use the `hint` watch subcommand for a hint. // I AM NOT DONE -pub fn times_two(num: i32) -> i32 { - num * 2 +pub struct ReportCard { + pub grade: f32, + pub student_name: String, + pub student_age: u8, +} + +impl ReportCard { + pub fn print(&self) -> String { + format!("{} ({}) - achieved a grade of {}", + &self.student_name, &self.student_age, &self.grade) + } } #[cfg(test)] @@ -18,13 +34,29 @@ mod tests { use super::*; #[test] - fn returns_twice_of_positive_numbers() { - assert_eq!(times_two(4), ???); + fn generate_numeric_report_card() { + let report_card = ReportCard { + grade: 2.1, + student_name: "Tom Wriggle".to_string(), + student_age: 12, + }; + assert_eq!( + report_card.print(), + "Tom Wriggle (12) - achieved a grade of 2.1" + ); } #[test] - fn returns_twice_of_negative_numbers() { - // TODO replace unimplemented!() with an assert for `times_two(-4)` - unimplemented!() + fn generate_alphabetic_report_card() { + // TODO: Make sure to change the grade here after you finish the exercise. + let report_card = ReportCard { + grade: 2.1, + student_name: "Gary Plotter".to_string(), + student_age: 11, + }; + assert_eq!( + report_card.print(), + "Gary Plotter (11) - achieved a grade of A+" + ); } } diff --git a/exercises/quiz4.rs b/exercises/quiz4.rs deleted file mode 100644 index 6c47480d..00000000 --- a/exercises/quiz4.rs +++ /dev/null @@ -1,23 +0,0 @@ -// quiz4.rs -// This quiz covers the sections: -// - Modules -// - Macros - -// Write a macro that passes the quiz! No hints this time, you can do it! - -// I AM NOT DONE - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_my_macro_world() { - assert_eq!(my_macro!("world!"), "Hello world!"); - } - - #[test] - fn test_my_macro_goodbye() { - assert_eq!(my_macro!("goodbye!"), "Hello goodbye!"); - } -} diff --git a/exercises/standard_library_types/arc1.rs b/exercises/standard_library_types/arc1.rs index f60061e7..93a27036 100644 --- a/exercises/standard_library_types/arc1.rs +++ b/exercises/standard_library_types/arc1.rs @@ -16,7 +16,7 @@ // Make this code compile by filling in a value for `shared_numbers` where the // first TODO comment is, and create an initial binding for `child_numbers` // where the second TODO comment is. Try not to create any copies of the `numbers` Vec! -// Execute `rustlings hint arc1` for hints :) +// Execute `rustlings hint arc1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/standard_library_types/box1.rs b/exercises/standard_library_types/box1.rs index f312f3d6..9d9237c1 100644 --- a/exercises/standard_library_types/box1.rs +++ b/exercises/standard_library_types/box1.rs @@ -14,7 +14,7 @@ // // Note: the tests should not be changed // -// Execute `rustlings hint box1` for hints :) +// Execute `rustlings hint box1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/standard_library_types/iterators1.rs b/exercises/standard_library_types/iterators1.rs index 5aa49b64..0379c6bb 100644 --- a/exercises/standard_library_types/iterators1.rs +++ b/exercises/standard_library_types/iterators1.rs @@ -6,7 +6,7 @@ // This module helps you get familiar with the structure of using an iterator and // how to go through elements within an iterable collection. // -// Execute `rustlings hint iterators1` for hints :D +// Execute `rustlings hint iterators1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/standard_library_types/iterators2.rs b/exercises/standard_library_types/iterators2.rs index 87b4eaa1..29c53afb 100644 --- a/exercises/standard_library_types/iterators2.rs +++ b/exercises/standard_library_types/iterators2.rs @@ -1,7 +1,7 @@ // iterators2.rs // In this exercise, you'll learn some of the unique advantages that iterators // can offer. Follow the steps to complete the exercise. -// As always, there are hints if you execute `rustlings hint iterators2`! +// Execute `rustlings hint iterators2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/standard_library_types/iterators3.rs b/exercises/standard_library_types/iterators3.rs index 8c66c05b..c97a6258 100644 --- a/exercises/standard_library_types/iterators3.rs +++ b/exercises/standard_library_types/iterators3.rs @@ -4,7 +4,7 @@ // 1. Complete the divide function to get the first four tests to pass. // 2. Get the remaining tests to pass by completing the result_with_list and // list_of_results functions. -// Execute `rustlings hint iterators3` to get some hints! +// Execute `rustlings hint iterators3` or use the `hint` watch subcommand for a hint. // I AM NOT DONE @@ -22,7 +22,9 @@ pub struct NotDivisibleError { // Calculate `a` divided by `b` if `a` is evenly divisible by `b`. // Otherwise, return a suitable error. -pub fn divide(a: i32, b: i32) -> Result {} +pub fn divide(a: i32, b: i32) -> Result { + todo!(); +} // Complete the function and return a value of the correct type so the test passes. // Desired output: Ok([1, 11, 1426, 3]) diff --git a/exercises/standard_library_types/iterators4.rs b/exercises/standard_library_types/iterators4.rs index 88862838..a02470ec 100644 --- a/exercises/standard_library_types/iterators4.rs +++ b/exercises/standard_library_types/iterators4.rs @@ -1,4 +1,5 @@ // iterators4.rs +// Execute `rustlings hint iterators4` or use the `hint` watch subcommand for a hint. // I AM NOT DONE @@ -18,6 +19,11 @@ pub fn factorial(num: u64) -> u64 { mod tests { use super::*; + #[test] + fn factorial_of_0() { + assert_eq!(1, factorial(0)); + } + #[test] fn factorial_of_1() { assert_eq!(1, factorial(1)); diff --git a/exercises/standard_library_types/iterators5.rs b/exercises/standard_library_types/iterators5.rs index 93f3ae11..0593d123 100644 --- a/exercises/standard_library_types/iterators5.rs +++ b/exercises/standard_library_types/iterators5.rs @@ -6,7 +6,7 @@ // imperative style for loops. Recreate this counting functionality using // iterators. Only the two iterator methods (count_iterator and // count_collection_iterator) need to be modified. -// Execute `rustlings hint iterators5` for hints. +// Execute `rustlings hint iterators5` or use the `hint` watch subcommand for a hint. // // Make the code compile and the tests pass. @@ -34,6 +34,7 @@ fn count_for(map: &HashMap, value: Progress) -> usize { fn count_iterator(map: &HashMap, value: Progress) -> usize { // map is a hashmap with String keys and Progress values. // map = { "variables1": Complete, "from_str": None, ... } + todo!(); } fn count_collection_for(collection: &[HashMap], value: Progress) -> usize { @@ -52,6 +53,7 @@ fn count_collection_iterator(collection: &[HashMap], value: Pr // collection is a slice of hashmaps. // collection = [{ "variables1": Complete, "from_str": None, ... }, // { "variables2": Complete, ... }, ... ] + todo!(); } #[cfg(test)] diff --git a/exercises/standard_library_types/mod.rs b/exercises/standard_library_types/mod.rs deleted file mode 100644 index b03acb92..00000000 --- a/exercises/standard_library_types/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -mod arc1; -mod box1; -mod iterators1; -mod iterators2; -mod iterators3; -mod iterators4; -mod iterators5; diff --git a/exercises/strings/mod.rs b/exercises/strings/mod.rs deleted file mode 100644 index b1b460bc..00000000 --- a/exercises/strings/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod strings1; -mod strings2; diff --git a/exercises/strings/strings1.rs b/exercises/strings/strings1.rs index 80902444..0de86a1d 100644 --- a/exercises/strings/strings1.rs +++ b/exercises/strings/strings1.rs @@ -1,6 +1,6 @@ // strings1.rs // Make me compile without changing the function signature! -// Execute `rustlings hint strings1` for hints ;) +// Execute `rustlings hint strings1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/strings/strings2.rs b/exercises/strings/strings2.rs index 5a2ce74a..0c48ec95 100644 --- a/exercises/strings/strings2.rs +++ b/exercises/strings/strings2.rs @@ -1,6 +1,6 @@ // strings2.rs // Make me compile without changing the function signature! -// Execute `rustlings hint strings2` for hints :) +// Execute `rustlings hint strings2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/strings/strings3.rs b/exercises/strings/strings3.rs new file mode 100644 index 00000000..9e25d307 --- /dev/null +++ b/exercises/strings/strings3.rs @@ -0,0 +1,43 @@ +// strings3.rs +// Execute `rustlings hint strings3` or use the `hint` watch subcommand for a hint. + +// I AM NOT DONE + +fn trim_me(input: &str) -> String { + // TODO: Remove whitespace from the end of a string! + ??? +} + +fn compose_me(input: &str) -> String { + // TODO: Add " world!" to the string! There's multiple ways to do this! + ??? +} + +fn replace_me(input: &str) -> String { + // TODO: Replace "cars" in the string with "balloons"! + ??? +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn trim_a_string() { + assert_eq!(trim_me("Hello! "), "Hello!"); + assert_eq!(trim_me(" What's up!"), "What's up!"); + assert_eq!(trim_me(" Hola! "), "Hola!"); + } + + #[test] + fn compose_a_string() { + assert_eq!(compose_me("Hello"), "Hello world!"); + assert_eq!(compose_me("Goodbye"), "Goodbye world!"); + } + + #[test] + fn replace_a_string() { + assert_eq!(replace_me("I think cars are cool"), "I think balloons are cool"); + assert_eq!(replace_me("I love to look at cars"), "I love to look at balloons"); + } +} diff --git a/exercises/strings/strings4.rs b/exercises/strings/strings4.rs new file mode 100644 index 00000000..c410b562 --- /dev/null +++ b/exercises/strings/strings4.rs @@ -0,0 +1,29 @@ +// strings4.rs + +// Ok, here are a bunch of values-- some are `String`s, some are `&str`s. Your +// task is to call one of these two functions on each value depending on what +// you think each value is. That is, add either `string_slice` or `string` +// before the parentheses on each line. If you're right, it will compile! +// No hints this time! + +// I AM NOT DONE + +fn string_slice(arg: &str) { + println!("{}", arg); +} +fn string(arg: String) { + println!("{}", arg); +} + +fn main() { + ???("blue"); + ???("red".to_string()); + ???(String::from("hi")); + ???("rust is fun!".to_owned()); + ???("nice weather".into()); + ???(format!("Interpolation {}", "Station")); + ???(&String::from("abc")[0..1]); + ???(" hello there ".trim()); + ???("Happy Monday!".to_string().replace("Mon", "Tues")); + ???("mY sHiFt KeY iS sTiCkY".to_lowercase()); +} diff --git a/exercises/structs/mod.rs b/exercises/structs/mod.rs deleted file mode 100644 index 214fed16..00000000 --- a/exercises/structs/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod structs1; -mod structs2; -mod structs3; diff --git a/exercises/structs/structs1.rs b/exercises/structs/structs1.rs index 6d0b2f49..0d91c469 100644 --- a/exercises/structs/structs1.rs +++ b/exercises/structs/structs1.rs @@ -1,5 +1,6 @@ // structs1.rs // Address all the TODOs to make the tests pass! +// Execute `rustlings hint structs1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE @@ -10,7 +11,7 @@ struct ColorClassicStruct { struct ColorTupleStruct(/* TODO: Something goes here */); #[derive(Debug)] -struct UnitStruct; +struct UnitLikeStruct; #[cfg(test)] mod tests { @@ -21,8 +22,9 @@ mod tests { // TODO: Instantiate a classic c struct! // let green = - assert_eq!(green.name, "green"); - assert_eq!(green.hex, "#00FF00"); + assert_eq!(green.red, 0); + assert_eq!(green.green, 255); + assert_eq!(green.blue, 0); } #[test] @@ -30,16 +32,17 @@ mod tests { // TODO: Instantiate a tuple struct! // let green = - assert_eq!(green.0, "green"); - assert_eq!(green.1, "#00FF00"); + assert_eq!(green.0, 0); + assert_eq!(green.1, 255); + assert_eq!(green.2, 0); } #[test] fn unit_structs() { - // TODO: Instantiate a unit struct! - // let unit_struct = - let message = format!("{:?}s are fun!", unit_struct); + // TODO: Instantiate a unit-like struct! + // let unit_like_struct = + let message = format!("{:?}s are fun!", unit_like_struct); - assert_eq!(message, "UnitStructs are fun!"); + assert_eq!(message, "UnitLikeStructs are fun!"); } } diff --git a/exercises/structs/structs2.rs b/exercises/structs/structs2.rs index f9c6427d..32e311fa 100644 --- a/exercises/structs/structs2.rs +++ b/exercises/structs/structs2.rs @@ -1,5 +1,6 @@ // structs2.rs // Address all the TODOs to make the tests pass! +// Execute `rustlings hint structs2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/structs/structs3.rs b/exercises/structs/structs3.rs index e84f2ebc..0b3615f4 100644 --- a/exercises/structs/structs3.rs +++ b/exercises/structs/structs3.rs @@ -2,7 +2,7 @@ // Structs contain data, but can also have logic. In this exercise we have // defined the Package struct and we want to test some logic attached to it. // Make the code compile and the tests pass! -// If you have issues execute `rustlings hint structs3` +// Execute `rustlings hint structs3` or use the `hint` watch subcommand for a hint. // I AM NOT DONE @@ -16,7 +16,7 @@ struct Package { impl Package { fn new(sender_country: String, recipient_country: String, weight_in_grams: i32) -> Package { if weight_in_grams <= 0 { - // panic statement goes here... + panic!("Can not ship a weightless package.") } else { Package { sender_country, diff --git a/exercises/tests/mod.rs b/exercises/tests/mod.rs deleted file mode 100644 index 489541be..00000000 --- a/exercises/tests/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod tests1; -mod tests2; -mod tests3; diff --git a/exercises/tests/tests1.rs b/exercises/tests/tests1.rs index 50586a19..8b6ea374 100644 --- a/exercises/tests/tests1.rs +++ b/exercises/tests/tests1.rs @@ -4,7 +4,8 @@ // rustlings run tests1 // This test has a problem with it -- make the test compile! Make the test -// pass! Make the test fail! Execute `rustlings hint tests1` for hints :) +// pass! Make the test fail! +// Execute `rustlings hint tests1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/tests/tests2.rs b/exercises/tests/tests2.rs index 0d981ad1..a5ac15b1 100644 --- a/exercises/tests/tests2.rs +++ b/exercises/tests/tests2.rs @@ -1,6 +1,7 @@ // tests2.rs // This test has a problem with it -- make the test compile! Make the test -// pass! Make the test fail! Execute `rustlings hint tests2` for hints :) +// pass! Make the test fail! +// Execute `rustlings hint tests2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/tests/tests3.rs b/exercises/tests/tests3.rs index 3424f940..196a81a0 100644 --- a/exercises/tests/tests3.rs +++ b/exercises/tests/tests3.rs @@ -2,7 +2,7 @@ // This test isn't testing our function -- make it do that in such a way that // the test passes. Then write a second test that tests whether we get the result // we expect to get when we call `is_even(5)`. -// Execute `rustlings hint tests3` for hints :) +// Execute `rustlings hint tests3` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/threads/mod.rs b/exercises/threads/mod.rs deleted file mode 100644 index 24d54007..00000000 --- a/exercises/threads/mod.rs +++ /dev/null @@ -1 +0,0 @@ -mod threads1; diff --git a/exercises/threads/threads1.rs b/exercises/threads/threads1.rs index f31b317e..e59f4ce4 100644 --- a/exercises/threads/threads1.rs +++ b/exercises/threads/threads1.rs @@ -1,32 +1,31 @@ // threads1.rs -// Make this compile! Execute `rustlings hint threads1` for hints :) -// The idea is the thread spawned on line 22 is completing jobs while the main thread is -// monitoring progress until 10 jobs are completed. Because of the difference between the -// spawned threads' sleep time, and the waiting threads sleep time, when you see 6 lines -// of "waiting..." and the program ends without timing out when running, -// you've got it :) +// Execute `rustlings hint threads1` or use the `hint` watch subcommand for a hint. +// This program should wait until all the spawned threads have finished before exiting. // I AM NOT DONE -use std::sync::Arc; use std::thread; use std::time::Duration; -struct JobStatus { - jobs_completed: u32, -} fn main() { - let status = Arc::new(JobStatus { jobs_completed: 0 }); - let status_shared = status.clone(); - thread::spawn(move || { - for _ in 0..10 { + + let mut handles = vec![]; + for i in 0..10 { + thread::spawn(move || { thread::sleep(Duration::from_millis(250)); - status_shared.jobs_completed += 1; - } - }); - while status.jobs_completed < 10 { - println!("waiting... "); - thread::sleep(Duration::from_millis(500)); + println!("thread {} is complete", i); + }); } + + let mut completed_threads = 0; + for handle in handles { + // TODO: a struct is returned from thread::spawn, can you use it? + completed_threads += 1; + } + + if completed_threads != 10 { + panic!("Oh no! All the spawned threads did not finish!"); + } + } diff --git a/exercises/threads/threads2.rs b/exercises/threads/threads2.rs new file mode 100644 index 00000000..d0f8578f --- /dev/null +++ b/exercises/threads/threads2.rs @@ -0,0 +1,34 @@ +// threads2.rs +// Execute `rustlings hint threads2` or use the `hint` watch subcommand for a hint. +// Building on the last exercise, we want all of the threads to complete their work but this time +// the spawned threads need to be in charge of updating a shared value: JobStatus.jobs_completed + +// I AM NOT DONE + +use std::sync::Arc; +use std::thread; +use std::time::Duration; + +struct JobStatus { + jobs_completed: u32, +} + +fn main() { + let status = Arc::new(JobStatus { jobs_completed: 0 }); + let mut handles = vec![]; + for _ in 0..10 { + let status_shared = status.clone(); + let handle = thread::spawn(move || { + thread::sleep(Duration::from_millis(250)); + // TODO: You must take an action before you update a shared value + status_shared.jobs_completed += 1; + }); + handles.push(handle); + } + for handle in handles { + handle.join().unwrap(); + // TODO: Print the value of the JobStatus.jobs_completed. Did you notice anything + // interesting in the output? Do you have to 'join' on all the handles? + println!("jobs completed {}", ???); + } +} diff --git a/exercises/threads/threads3.rs b/exercises/threads/threads3.rs new file mode 100644 index 00000000..27e99088 --- /dev/null +++ b/exercises/threads/threads3.rs @@ -0,0 +1,64 @@ +// threads3.rs +// Execute `rustlings hint threads3` or use the `hint` watch subcommand for a hint. + +// I AM NOT DONE + +use std::sync::mpsc; +use std::sync::Arc; +use std::thread; +use std::time::Duration; + +struct Queue { + length: u32, + first_half: Vec, + second_half: Vec, +} + +impl Queue { + fn new() -> Self { + Queue { + length: 10, + first_half: vec![1, 2, 3, 4, 5], + second_half: vec![6, 7, 8, 9, 10], + } + } +} + +fn send_tx(q: Queue, tx: mpsc::Sender) -> () { + let qc = Arc::new(q); + let qc1 = qc.clone(); + let qc2 = qc.clone(); + + thread::spawn(move || { + for val in &qc1.first_half { + println!("sending {:?}", val); + tx.send(*val).unwrap(); + thread::sleep(Duration::from_secs(1)); + } + }); + + thread::spawn(move || { + for val in &qc2.second_half { + println!("sending {:?}", val); + tx.send(*val).unwrap(); + thread::sleep(Duration::from_secs(1)); + } + }); +} + +fn main() { + let (tx, rx) = mpsc::channel(); + let queue = Queue::new(); + let queue_length = queue.length; + + send_tx(queue, tx); + + let mut total_received: u32 = 0; + for received in rx { + println!("Got: {}", received); + total_received += 1; + } + + println!("total numbers received: {}", total_received); + assert_eq!(total_received, queue_length) +} diff --git a/exercises/traits/mod.rs b/exercises/traits/mod.rs deleted file mode 100644 index 6f0a9c3f..00000000 --- a/exercises/traits/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod traits1; -mod traits2; diff --git a/exercises/traits/traits1.rs b/exercises/traits/traits1.rs index 15e08f24..5b9d8d50 100644 --- a/exercises/traits/traits1.rs +++ b/exercises/traits/traits1.rs @@ -7,6 +7,7 @@ // The trait AppendBar has only one function, // which appends "Bar" to any object // implementing this trait. +// Execute `rustlings hint traits1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/traits/traits2.rs b/exercises/traits/traits2.rs index 916c3c4b..708bb19a 100644 --- a/exercises/traits/traits2.rs +++ b/exercises/traits/traits2.rs @@ -9,6 +9,7 @@ // // No boiler plate code this time, // you can do this! +// Execute `rustlings hint traits2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/traits/traits3.rs b/exercises/traits/traits3.rs new file mode 100644 index 00000000..6d2fd6c3 --- /dev/null +++ b/exercises/traits/traits3.rs @@ -0,0 +1,41 @@ +// traits3.rs +// +// Your task is to implement the Licensed trait for +// both structures and have them return the same +// information without writing the same function twice. +// +// Consider what you can add to the Licensed trait. +// Execute `rustlings hint traits3` or use the `hint` watch subcommand for a hint. + +// I AM NOT DONE + +pub trait Licensed { + fn licensing_info(&self) -> String; +} + +struct SomeSoftware { + version_number: i32, +} + +struct OtherSoftware { + version_number: String, +} + +impl Licensed for SomeSoftware {} // Don't edit this line +impl Licensed for OtherSoftware {} // Don't edit this line + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn is_licensing_info_the_same() { + let licensing_info = String::from("Some information"); + let some_software = SomeSoftware { version_number: 1 }; + let other_software = OtherSoftware { + version_number: "v2.0.0".to_string(), + }; + assert_eq!(some_software.licensing_info(), licensing_info); + assert_eq!(other_software.licensing_info(), licensing_info); + } +} diff --git a/exercises/traits/traits4.rs b/exercises/traits/traits4.rs new file mode 100644 index 00000000..280aaade --- /dev/null +++ b/exercises/traits/traits4.rs @@ -0,0 +1,45 @@ +// traits4.rs +// +// Your task is to replace the '??' sections so the code compiles. +// Don't change any line other than 21. +// Execute `rustlings hint traits4` or use the `hint` watch subcommand for a hint. + +// I AM NOT DONE + +pub trait Licensed { + fn licensing_info(&self) -> String { + "some information".to_string() + } +} + +struct SomeSoftware {} + +struct OtherSoftware {} + +impl Licensed for SomeSoftware {} +impl Licensed for OtherSoftware {} + +fn compare_license_types(software: ??, software_two: ??) -> bool { + software.licensing_info() == software_two.licensing_info() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn compare_license_information() { + let some_software = SomeSoftware {}; + let other_software = OtherSoftware {}; + + assert!(compare_license_types(some_software, other_software)); + } + + #[test] + fn compare_license_information_backwards() { + let some_software = SomeSoftware {}; + let other_software = OtherSoftware {}; + + assert!(compare_license_types(other_software, some_software)); + } +} diff --git a/exercises/traits/traits5.rs b/exercises/traits/traits5.rs new file mode 100644 index 00000000..290c0479 --- /dev/null +++ b/exercises/traits/traits5.rs @@ -0,0 +1,32 @@ +// traits5.rs +// +// Your task is to replace the '??' sections so the code compiles. +// Don't change any line other than 27. +// Execute `rustlings hint traits5` or use the `hint` watch subcommand for a hint. + +// I AM NOT DONE + +pub trait SomeTrait { + fn some_function(&self) -> bool { + true + } +} + +pub trait OtherTrait { + fn other_function(&self) -> bool { + true + } +} + +struct SomeStruct { + name: String, +} + +impl SomeTrait for SomeStruct {} +impl OtherTrait for SomeStruct {} + +fn some_func(item: ??) -> bool { + item.some_function() && item.other_function() +} + +fn main() {} diff --git a/exercises/variables/mod.rs b/exercises/variables/mod.rs deleted file mode 100644 index a25f477e..00000000 --- a/exercises/variables/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -mod variables1; -mod variables2; -mod variables3; -mod variables4; -mod variables5; -mod variables6; diff --git a/exercises/variables/variables1.rs b/exercises/variables/variables1.rs index d1af8311..f4d182ac 100644 --- a/exercises/variables/variables1.rs +++ b/exercises/variables/variables1.rs @@ -1,6 +1,6 @@ // variables1.rs // Make me compile! -// Execute the command `rustlings hint variables1` if you want a hint :) +// Execute `rustlings hint variables1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/variables/variables2.rs b/exercises/variables/variables2.rs index 7774a8fb..641aeb8e 100644 --- a/exercises/variables/variables2.rs +++ b/exercises/variables/variables2.rs @@ -1,13 +1,13 @@ // variables2.rs -// Make me compile! Execute the command `rustlings hint variables2` if you want a hint :) +// Execute `rustlings hint variables2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE fn main() { let x; if x == 10 { - println!("Ten!"); + println!("x is ten!"); } else { - println!("Not ten!"); + println!("x is not ten!"); } } diff --git a/exercises/variables/variables3.rs b/exercises/variables/variables3.rs index 30ec48ff..819b1bc7 100644 --- a/exercises/variables/variables3.rs +++ b/exercises/variables/variables3.rs @@ -1,11 +1,9 @@ // variables3.rs -// Make me compile! Execute the command `rustlings hint variables3` if you want a hint :) +// Execute `rustlings hint variables3` or use the `hint` watch subcommand for a hint. // I AM NOT DONE fn main() { - let x = 3; - println!("Number {}", x); - x = 5; // don't change this line + let x: i32; println!("Number {}", x); } diff --git a/exercises/variables/variables4.rs b/exercises/variables/variables4.rs index 77f1e9ab..54491b0a 100644 --- a/exercises/variables/variables4.rs +++ b/exercises/variables/variables4.rs @@ -1,9 +1,11 @@ // variables4.rs -// Make me compile! Execute the command `rustlings hint variables4` if you want a hint :) +// Execute `rustlings hint variables4` or use the `hint` watch subcommand for a hint. // I AM NOT DONE fn main() { - let x: i32; + let x = 3; + println!("Number {}", x); + x = 5; // don't change this line println!("Number {}", x); } diff --git a/exercises/variables/variables5.rs b/exercises/variables/variables5.rs index 175eebb3..0e670d2a 100644 --- a/exercises/variables/variables5.rs +++ b/exercises/variables/variables5.rs @@ -1,11 +1,11 @@ // variables5.rs -// Make me compile! Execute the command `rustlings hint variables5` if you want a hint :) +// Execute `rustlings hint variables5` or use the `hint` watch subcommand for a hint. // I AM NOT DONE fn main() { let number = "T-H-R-E-E"; // don't change this line println!("Spell a Number : {}", number); - number = 3; + number = 3; // don't rename this variable println!("Number plus two is : {}", number + 2); } diff --git a/exercises/variables/variables6.rs b/exercises/variables/variables6.rs index 98666914..a8520122 100644 --- a/exercises/variables/variables6.rs +++ b/exercises/variables/variables6.rs @@ -1,5 +1,5 @@ // variables6.rs -// Make me compile! Execute the command `rustlings hint variables6` if you want a hint :) +// Execute `rustlings hint variables6` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/vecs/README.md b/exercises/vecs/README.md new file mode 100644 index 00000000..ebe90bf3 --- /dev/null +++ b/exercises/vecs/README.md @@ -0,0 +1,15 @@ +# Vectors + +Vectors are one of the most-used Rust data structures. In other programming +languages, they'd simply be called Arrays, but since Rust operates on a +bit of a lower level, an array in Rust is stored on the stack (meaning it +can't grow or shrink, and the size needs to be known at compile time), +and a Vector is stored in the heap (where these restrictions do not apply). + +Vectors are a bit of a later chapter in the book, but we think that they're +useful enough to talk about them a bit earlier. We shall be talking about +the other useful data structure, hash maps, later. + +## Further information + +- [Storing Lists of Values with Vectors](https://doc.rust-lang.org/stable/book/ch08-01-vectors.html) diff --git a/exercises/collections/vec1.rs b/exercises/vecs/vecs1.rs similarity index 84% rename from exercises/collections/vec1.rs rename to exercises/vecs/vecs1.rs index b144fb94..4e8c4cbb 100644 --- a/exercises/collections/vec1.rs +++ b/exercises/vecs/vecs1.rs @@ -1,8 +1,8 @@ -// vec1.rs +// vecs1.rs // Your task is to create a `Vec` which holds the exact same elements // as in the array `a`. // Make me compile and pass the test! -// Execute the command `rustlings hint vec1` if you need hints. +// Execute `rustlings hint vecs1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/collections/vec2.rs b/exercises/vecs/vecs2.rs similarity index 56% rename from exercises/collections/vec2.rs rename to exercises/vecs/vecs2.rs index 6595e401..5bea09a2 100644 --- a/exercises/collections/vec2.rs +++ b/exercises/vecs/vecs2.rs @@ -1,11 +1,10 @@ -// vec2.rs +// vecs2.rs // A Vec of even numbers is given. Your task is to complete the loop // so that each number in the Vec is multiplied by 2. // // Make me pass the test! // -// Execute the command `rustlings hint vec2` if you need -// hints. +// Execute `rustlings hint vecs2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE @@ -13,12 +12,21 @@ fn vec_loop(mut v: Vec) -> Vec { for i in v.iter_mut() { // TODO: Fill this up so that each element in the Vec `v` is // multiplied by 2. + ??? } // At this point, `v` should be equal to [4, 8, 12, 16, 20]. v } +fn vec_map(v: &Vec) -> Vec { + v.iter().map(|num| { + // TODO: Do the same thing as above - but instead of mutating the + // Vec, you can just return the new number! + ??? + }).collect() +} + #[cfg(test)] mod tests { use super::*; @@ -30,4 +38,12 @@ mod tests { assert_eq!(ans, v.iter().map(|x| x * 2).collect::>()); } + + #[test] + fn test_vec_map() { + let v: Vec = (1..).filter(|x| x % 2 == 0).take(5).collect(); + let ans = vec_map(&v); + + assert_eq!(ans, v.iter().map(|x| x * 2).collect::>()); + } } diff --git a/info.toml b/info.toml index a9608e65..5b7b9b43 100644 --- a/info.toml +++ b/info.toml @@ -5,7 +5,8 @@ name = "intro1" path = "exercises/intro/intro1.rs" mode = "compile" hint = """ -Remove the I AM NOT DONE comment to move on to the next exercise.""" +Remove the I AM NOT DONE comment in the exercises/intro/intro1.rs file +to move on to the next exercise.""" [[exercises]] name = "intro2" @@ -21,7 +22,7 @@ name = "variables1" path = "exercises/variables/variables1.rs" mode = "compile" hint = """ -Hint: The declaration on line 8 is missing a keyword that is needed in Rust +The declaration on line 8 is missing a keyword that is needed in Rust to create a new variable binding.""" [[exercises]] @@ -42,20 +43,20 @@ name = "variables3" path = "exercises/variables/variables3.rs" mode = "compile" hint = """ -In Rust, variable bindings are immutable by default. But here we're trying -to reassign a different value to x! There's a keyword we can use to make -a variable binding mutable instead.""" +Oops! In this exercise, we have a variable binding that we've created on +line 7, and we're trying to use it on line 8, but we haven't given it a +value. We can't print out something that isn't there; try giving x a value! +This is an error that can cause bugs that's very easy to make in any +programming language -- thankfully the Rust compiler has caught this for us!""" [[exercises]] name = "variables4" path = "exercises/variables/variables4.rs" mode = "compile" hint = """ -Oops! In this exercise, we have a variable binding that we've created on -line 7, and we're trying to use it on line 8, but we haven't given it a -value. We can't print out something that isn't there; try giving x a value! -This is an error that can cause bugs that's very easy to make in any -programming language -- thankfully the Rust compiler has caught this for us!""" +In Rust, variable bindings are immutable by default. But here we're trying +to reassign a different value to x! There's a keyword we can use to make +a variable binding mutable instead.""" [[exercises]] name = "variables5" @@ -78,12 +79,12 @@ path = "exercises/variables/variables6.rs" mode = "compile" hint = """ We know about variables and mutability, but there is another important type of -variable available; constants. +variable available: constants. Constants are always immutable and they are declared with keyword 'const' rather than keyword 'let'. Constants types must also always be annotated. -Read more about constants under 'Differences Between Variables and Constants' in the book's section 'Variables and Mutability': +Read more about constants and the differences between variables and constants under 'Constants' in the book's section 'Variables and Mutability': https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#constants """ @@ -113,7 +114,9 @@ path = "exercises/functions/functions3.rs" mode = "compile" hint = """ This time, the function *declaration* is okay, but there's something wrong -with the place where we're calling the function.""" +with the place where we're calling the function. +As a reminder, you can freely play around with different solutions in Rustlings! +Watch mode will only jump to the next exercise if you remove the I AM NOT DONE comment.""" [[exercises]] name = "functions4" @@ -122,7 +125,10 @@ mode = "compile" hint = """ The error message points to line 14 and says it expects a type after the `->`. This is where the function's return type should be-- take a look at -the `is_even` function for an example!""" +the `is_even` function for an example! + +Also: Did you figure out that, technically, u32 would be the more fitting type +for the prices here, since they can't be negative? If so, kudos!""" [[exercises]] name = "functions5" @@ -161,7 +167,7 @@ For that first compiler error, it's important in Rust that each conditional block returns the same type! To get the tests passing, you will need a couple conditions checking different input values.""" -# TEST 1 +# QUIZ 1 [[exercises]] name = "quiz1" @@ -169,6 +175,102 @@ path = "exercises/quiz1.rs" mode = "test" hint = "No hints this time ;)" +# PRIMITIVE TYPES + +[[exercises]] +name = "primitive_types1" +path = "exercises/primitive_types/primitive_types1.rs" +mode = "compile" +hint = "No hints this time ;)" + +[[exercises]] +name = "primitive_types2" +path = "exercises/primitive_types/primitive_types2.rs" +mode = "compile" +hint = "No hints this time ;)" + +[[exercises]] +name = "primitive_types3" +path = "exercises/primitive_types/primitive_types3.rs" +mode = "compile" +hint = """ +There's a shorthand to initialize Arrays with a certain size that does not +require you to type in 100 items (but you certainly can if you want!). +For example, you can do: +let array = ["Are we there yet?"; 10]; + +Bonus: what are some other things you could have that would return true +for `a.len() >= 100`?""" + +[[exercises]] +name = "primitive_types4" +path = "exercises/primitive_types/primitive_types4.rs" +mode = "test" +hint = """ +Take a look at the Understanding Ownership -> Slices -> Other Slices section of the book: +https://doc.rust-lang.org/book/ch04-03-slices.html +and use the starting and ending indices of the items in the Array +that you want to end up in the slice. + +If you're curious why the first argument of `assert_eq!` does not +have an ampersand for a reference since the second argument is a +reference, take a look at the coercion chapter of the nomicon: +https://doc.rust-lang.org/nomicon/coercions.html""" + +[[exercises]] +name = "primitive_types5" +path = "exercises/primitive_types/primitive_types5.rs" +mode = "compile" +hint = """ +Take a look at the Data Types -> The Tuple Type section of the book: +https://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type +Particularly the part about destructuring (second to last example in the section). +You'll need to make a pattern to bind `name` and `age` to the appropriate parts +of the tuple. You can do it!!""" + +[[exercises]] +name = "primitive_types6" +path = "exercises/primitive_types/primitive_types6.rs" +mode = "test" +hint = """ +While you could use a destructuring `let` for the tuple here, try +indexing into it instead, as explained in the last example of the +Data Types -> The Tuple Type section of the book: +https://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type +Now you have another tool in your toolbox!""" + +# VECS + +[[exercises]] +name = "vecs1" +path = "exercises/vecs/vecs1.rs" +mode = "test" +hint = """ +In Rust, there are two ways to define a Vector. +1. One way is to use the `Vec::new()` function to create a new vector + and fill it with the `push()` method. +2. The second way, which is simpler is to use the `vec![]` macro and + define your elements inside the square brackets. +Check this chapter: https://doc.rust-lang.org/stable/book/ch08-01-vectors.html +of the Rust book to learn more. +""" + +[[exercises]] +name = "vecs2" +path = "exercises/vecs/vecs2.rs" +mode = "test" +hint = """ +Hint 1: `i` is each element from the Vec as they are being iterated. Can you try +multiplying this? + +Hint 2: For the first function, there's a way to directly access the numbers stored +in the Vec, using the * dereference operator. You can both access and write to the +number that way. + +After you've completed both functions, decide for yourself which approach you like +better. What do you think is the more commonly used pattern under Rust developers? +""" + # MOVE SEMANTICS [[exercises]] @@ -178,24 +280,27 @@ mode = "compile" hint = """ So you've got the "cannot borrow immutable local variable `vec1` as mutable" error on line 13, right? The fix for this is going to be adding one keyword, and the addition is NOT on line 13 -where the error is.""" +where the error is. + +Also: Try accessing `vec0` after having called `fill_vec()`. See what happens!""" [[exercises]] name = "move_semantics2" path = "exercises/move_semantics/move_semantics2.rs" mode = "compile" hint = """ -So `vec0` is being *moved* into the function `fill_vec` when we call it on -line 10, which means it gets dropped at the end of `fill_vec`, which means we -can't use `vec0` again on line 13 (or anywhere else in `main` after the -`fill_vec` call for that matter). We could fix this in a few ways, try them -all! +So, `vec0` is passed into the `fill_vec` function as an argument. In Rust, +when an argument is passed to a function and it's not explicitly returned, +you can't use the original variable anymore. We call this "moving" a variable. +Variables that are moved into a function (or block scope) and aren't explicitly +returned get "dropped" at the end of that function. This is also what happens here. +There's a few ways to fix this, try them all if you want: 1. Make another, separate version of the data that's in `vec0` and pass that to `fill_vec` instead. 2. Make `fill_vec` borrow its argument instead of taking ownership of it, and then copy the data within the function in order to return an owned `Vec` -3. Make `fill_vec` *mutably* borrow its argument (which will need to be +3. Make `fill_vec` *mutably* borrow a reference to its argument (which will need to be mutable), modify it directly, then not return anything. Then you can get rid of `vec1` entirely -- note that this will change what gets printed by the first `println!`""" @@ -252,70 +357,6 @@ Can you figure out how? Another hint: it has to do with the `&` character.""" -# PRIMITIVE TYPES - -[[exercises]] -name = "primitive_types1" -path = "exercises/primitive_types/primitive_types1.rs" -mode = "compile" -hint = "No hints this time ;)" - -[[exercises]] -name = "primitive_types2" -path = "exercises/primitive_types/primitive_types2.rs" -mode = "compile" -hint = "No hints this time ;)" - -[[exercises]] -name = "primitive_types3" -path = "exercises/primitive_types/primitive_types3.rs" -mode = "compile" -hint = """ -There's a shorthand to initialize Arrays with a certain size that does not -require you to type in 100 items (but you certainly can if you want!). -For example, you can do: -let array = ["Are we there yet?"; 10]; - -Bonus: what are some other things you could have that would return true -for `a.len() >= 100`?""" - -[[exercises]] -name = "primitive_types4" -path = "exercises/primitive_types/primitive_types4.rs" -mode = "test" -hint = """ -Take a look at the Understanding Ownership -> Slices -> Other Slices section of the book: -https://doc.rust-lang.org/book/ch04-03-slices.html -and use the starting and ending indices of the items in the Array -that you want to end up in the slice. - -If you're curious why the first argument of `assert_eq!` does not -have an ampersand for a reference since the second argument is a -reference, take a look at the Deref coercions section of the book: -https://doc.rust-lang.org/book/ch15-02-deref.html""" - -[[exercises]] -name = "primitive_types5" -path = "exercises/primitive_types/primitive_types5.rs" -mode = "compile" -hint = """ -Take a look at the Data Types -> The Tuple Type section of the book: -https://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type -Particularly the part about destructuring (second to last example in the section). -You'll need to make a pattern to bind `name` and `age` to the appropriate parts -of the tuple. You can do it!!""" - -[[exercises]] -name = "primitive_types6" -path = "exercises/primitive_types/primitive_types6.rs" -mode = "test" -hint = """ -While you could use a destructuring `let` for the tuple here, try -indexing into it instead, as explained in the last example of the -Data Types -> The Tuple Type section of the book: -https://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type -Now you have another tool in your toolbox!""" - # STRUCTS [[exercises]] @@ -326,7 +367,7 @@ hint = """ Rust has more than one type of struct. Three actually, all variants are used to package related data together. There are normal (or classic) structs. These are named collections of related data stored in fields. Tuple structs are basically just named tuples. -Finally, Unit structs. These don't have any fields and are useful for generics. +Finally, Unit-like structs. These don't have any fields and are useful for generics. In this exercise you need to complete and implement one of each kind. Read more about structs in The Book: https://doc.rust-lang.org/book/ch05-01-defining-structs.html""" @@ -359,21 +400,64 @@ Have a look in The Book, to find out more about method implementations: https:// name = "enums1" path = "exercises/enums/enums1.rs" mode = "compile" -hint = """ -Hint: The declaration of the enumeration type has not been defined yet.""" +hint = "No hints this time ;)" [[exercises]] name = "enums2" path = "exercises/enums/enums2.rs" mode = "compile" hint = """ -Hint: you can create enumerations that have different variants with different types +You can create enumerations that have different variants with different types such as no data, anonymous structs, a single string, tuples, ...etc""" [[exercises]] name = "enums3" path = "exercises/enums/enums3.rs" mode = "test" +hint = """ +As a first step, you can define enums to compile this code without errors. +and then create a match expression in `process()`. +Note that you need to deconstruct some message variants +in the match expression to get value in the variant.""" + +# STRINGS + +[[exercises]] +name = "strings1" +path = "exercises/strings/strings1.rs" +mode = "compile" +hint = """ +The `current_favorite_color` function is currently returning a string slice with the `'static` +lifetime. We know this because the data of the string lives in our code itself -- it doesn't +come from a file or user input or another program -- so it will live as long as our program +lives. But it is still a string slice. There's one way to create a `String` by converting a +string slice covered in the Strings chapter of the book, and another way that uses the `From` +trait.""" + +[[exercises]] +name = "strings2" +path = "exercises/strings/strings2.rs" +mode = "compile" +hint = """ +Yes, it would be really easy to fix this by just changing the value bound to `word` to be a +string slice instead of a `String`, wouldn't it?? There is a way to add one character to line +9, though, that will coerce the `String` into a string slice.""" + +[[exercises]] +name = "strings3" +path = "exercises/strings/strings3.rs" +mode = "test" +hint = """ +There's tons of useful standard library functions for strings. Let's try and use some of +them! + +For the compose_me method: You can either use the `format!` macro, or convert the string +slice into an owned string, which you can then freely extend.""" + +[[exercises]] +name = "strings4" +path = "exercises/strings/strings4.rs" +mode = "compile" hint = "No hints this time ;)" # MODULES @@ -406,35 +490,11 @@ UNIX_EPOCH and SystemTime are declared in the std::time module. Add a use statem for these two to bring them into scope. You can use nested paths or the glob operator to bring these two in using only one line.""" -# COLLECTIONS +# HASHMAPS [[exercises]] -name = "vec1" -path = "exercises/collections/vec1.rs" -mode = "test" -hint = """ -In Rust, there are two ways to define a Vector. -1. One way is to use the `Vec::new()` function to create a new vector - and fill it with the `push()` method. -2. The second way, which is simpler is to use the `vec![]` macro and - define your elements inside the square brackets. -Check this chapter: https://doc.rust-lang.org/stable/book/ch08-01-vectors.html -of the Rust book to learn more. -""" - -[[exercises]] -name = "vec2" -path = "exercises/collections/vec2.rs" -mode = "test" -hint = """ -Hint 1: `i` is each element from the Vec as they are being iterated. - Can you try multiplying this? -Hint 2: Check the suggestion from the compiler error ;) -""" - -[[exercises]] -name = "hashmap1" -path = "exercises/collections/hashmap1.rs" +name = "hashmaps1" +path = "exercises/hashmaps/hashmaps1.rs" mode = "test" hint = """ Hint 1: Take a look at the return type of the function to figure out @@ -444,45 +504,68 @@ Hint 2: Number of fruits should be at least 5. And you have to put """ [[exercises]] -name = "hashmap2" -path = "exercises/collections/hashmap2.rs" +name = "hashmaps2" +path = "exercises/hashmaps/hashmaps2.rs" mode = "test" hint = """ Use the `entry()` and `or_insert()` methods of `HashMap` to achieve this. Learn more at https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value """ -# STRINGS - [[exercises]] -name = "strings1" -path = "exercises/strings/strings1.rs" -mode = "compile" +name = "hashmaps3" +path = "exercises/hashmaps/hashmaps3.rs" +mode = "test" hint = """ -The `current_favorite_color` function is currently returning a string slice with the `'static` -lifetime. We know this because the data of the string lives in our code itself -- it doesn't -come from a file or user input or another program -- so it will live as long as our program -lives. But it is still a string slice. There's one way to create a `String` by converting a -string slice covered in the Strings chapter of the book, and another way that uses the `From` -trait.""" +Hint 1: Use the `entry()` and `or_insert()` methods of `HashMap` to insert entries corresponding to each team in the scores table. +Learn more at https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value +Hint 2: If there is already an entry for a given key, the value returned by `entry()` can be updated based on the existing value. +Learn more at https://doc.rust-lang.org/book/ch08-03-hash-maps.html#updating-a-value-based-on-the-old-value +""" -[[exercises]] -name = "strings2" -path = "exercises/strings/strings2.rs" -mode = "compile" -hint = """ -Yes, it would be really easy to fix this by just changing the value bound to `word` to be a -string slice instead of a `String`, wouldn't it?? There is a way to add one character to line -9, though, that will coerce the `String` into a string slice.""" - -# TEST 2 +# QUIZ 2 [[exercises]] name = "quiz2" path = "exercises/quiz2.rs" -mode = "compile" +mode = "test" hint = "No hints this time ;)" +# OPTIONS + +[[exercises]] +name = "options1" +path = "exercises/options/options1.rs" +mode = "test" +hint = """ +Options can have a Some value, with an inner value, or a None value, without an inner value. +There's multiple ways to get at the inner value, you can use unwrap, or pattern match. Unwrapping +is the easiest, but how do you do it safely so that it doesn't panic in your face later?""" + +[[exercises]] +name = "options2" +path = "exercises/options/options2.rs" +mode = "compile" +hint = """ +check out: +https://doc.rust-lang.org/rust-by-example/flow_control/if_let.html +https://doc.rust-lang.org/rust-by-example/flow_control/while_let.html + +Remember that Options can be stacked in if let and while let. +For example: Some(Some(variable)) = variable2 +Also see Option::flatten +""" + +[[exercises]] +name = "options3" +path = "exercises/options/options3.rs" +mode = "compile" +hint = """ +The compiler says a partial move happened in the `match` +statement. How can this be avoided? The compiler shows the correction +needed. After making the correction as suggested by the compiler, do +read: https://doc.rust-lang.org/std/keyword.ref.html""" + # ERROR HANDLING [[exercises]] @@ -520,7 +603,10 @@ name = "errors3" path = "exercises/error_handling/errors3.rs" mode = "compile" hint = """ -If other functions can return a `Result`, why shouldn't `main`?""" +If other functions can return a `Result`, why shouldn't `main`? It's a fairly common +convention to return something like Result<(), ErrorType> from your main function. +The unit (`()`) type is there because nothing is really needed in terms of positive +results.""" [[exercises]] name = "errors4" @@ -536,22 +622,17 @@ name = "errors5" path = "exercises/error_handling/errors5.rs" mode = "compile" hint = """ -Hint: There are two different possible `Result` types produced within -`main()`, which are propagated using `?` operators. How do we declare a -return type from `main()` that allows both? +There are two different possible `Result` types produced within `main()`, which are +propagated using `?` operators. How do we declare a return type from `main()` that allows both? + +Under the hood, the `?` operator calls `From::from` on the error value to convert it to a boxed +trait object, a `Box`. This boxed trait object is polymorphic, and since all +errors implement the `error:Error` trait, we can capture lots of different errors in one "Box" +object. -Another hint: under the hood, the `?` operator calls `From::from` -on the error value to convert it to a boxed trait object, a -`Box`, which is polymorphic-- that means that lots of -different kinds of errors can be returned from the same function because -all errors act the same since they all implement the `error::Error` trait. Check out this section of the book: https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator -This exercise uses some concepts that we won't get to until later in the -course, like `Box` and the `From` trait. It's not important to understand -them in detail right now, but you can read ahead if you like. - Read more about boxing errors: https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/boxing_errors.html @@ -600,61 +681,6 @@ Maybe we could update the explicit references to this data type somehow? If you are still stuck https://doc.rust-lang.org/stable/book/ch10-01-syntax.html#in-method-definitions """ -[[exercises]] -name = "generics3" -path = "exercises/generics/generics3.rs" -mode = "test" -hint = """ -To find the best solution to this challenge you're going to need to think back to your -knowledge of traits, specifically Trait Bound Syntax - you may also need this: "use std::fmt::Display;" - -This is definitely harder than the last two exercises! You need to think about not only making the -ReportCard struct generic, but also the correct property - you will need to change the implementation -of the struct slightly too...you can do it! -""" - -# OPTIONS - -[[exercises]] -name = "option1" -path = "exercises/option/option1.rs" -mode = "compile" -hint = """ -Hint 1: Check out some functions of Option: -is_some -is_none -unwrap - -and: -pattern matching - -Hint 2: There are no sensible defaults for the value of an Array; the values need to be filled before use. -""" - -[[exercises]] -name = "option2" -path = "exercises/option/option2.rs" -mode = "compile" -hint = """ -check out: -https://doc.rust-lang.org/rust-by-example/flow_control/if_let.html -https://doc.rust-lang.org/rust-by-example/flow_control/while_let.html - -Remember that Options can be stacked in if let and while let. -For example: Some(Some(variable)) = variable2 -Also see Option::flatten -""" - -[[exercises]] -name = "option3" -path = "exercises/option/option3.rs" -mode = "compile" -hint = """ -The compiler says a partial move happened in the `match` -statement. How can this be avoided? The compiler shows the correction -needed. After making the correction as suggested by the compiler, do -read: https://doc.rust-lang.org/std/keyword.ref.html""" - # TRAITS [[exercises]] @@ -672,11 +698,56 @@ path = "exercises/traits/traits2.rs" mode = "test" hint = """ Notice how the trait takes ownership of 'self',and returns `Self'. -Try mutating the incoming string vector. +Try mutating the incoming string vector. Have a look at the tests to see +what the result should look like! Vectors provide suitable methods for adding an element at the end. See the documentation at: https://doc.rust-lang.org/std/vec/struct.Vec.html""" +[[exercises]] +name = "traits3" +path = "exercises/traits/traits3.rs" +mode = "test" +hint = """ +Traits can have a default implementation for functions. Structs that implement +the trait can then use the default version of these functions if they choose not +implement the function themselves. + +See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#default-implementations +""" + +[[exercises]] +name = "traits4" +path = "exercises/traits/traits4.rs" +mode = "test" +hint = """ +Instead of using concrete types as parameters you can use traits. Try replacing the +'??' with 'impl ' + +See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters +""" + +[[exercises]] +name = "traits5" +path = "exercises/traits/traits5.rs" +mode = "compile" +hint = """ +To ensure a paramter implements multiple traits use the '+ syntax'. Try replacing the +'??' with 'impl <> + <>'. + +See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#specifying-multiple-trait-bounds-with-the--syntax +""" + +# QUIZ 3 + +[[exercises]] +name = "quiz3" +path = "exercises/quiz3.rs" +mode = "test" +hint = """ +To find the best solution to this challenge you're going to need to think back to your +knowledge of traits, specifically Trait Bound Syntax - you may also need this: "use std::fmt::Display;"""" + # TESTS [[exercises]] @@ -709,50 +780,31 @@ You can call a function right where you're passing arguments to `assert!` -- so something like `assert!(having_fun())`. If you want to check that you indeed get false, you can negate the result of what you're doing using `!`, like `assert!(!having_fun())`.""" -# TEST 3 +# LIFETIMES [[exercises]] -name = "quiz3" -path = "exercises/quiz3.rs" -mode = "test" -hint = "No hints this time ;)" - -# STANDARD LIBRARY TYPES - -[[exercises]] -name = "box1" -path = "exercises/standard_library_types/box1.rs" -mode = "test" -hint = """ -Step 1 -The compiler's message should help: since we cannot store the value of the actual type -when working with recursive types, we need to store a reference (pointer) to its value. -We should, therefore, place our `List` inside a `Box`. More details in the book here: -https://doc.rust-lang.org/book/ch15-01-box.html#enabling-recursive-types-with-boxes - -Step 2 -Creating an empty list should be fairly straightforward (hint: peek at the assertions). -For a non-empty list keep in mind that we want to use our Cons "list builder". -Although the current list is one of integers (i32), feel free to change the definition -and try other types! -""" - -[[exercises]] -name = "arc1" -path = "exercises/standard_library_types/arc1.rs" +name = "lifetimes1" +path = "exercises/lifetimes/lifetimes1.rs" mode = "compile" hint = """ -Make `shared_numbers` be an `Arc` from the numbers vector. Then, in order -to avoid creating a copy of `numbers`, you'll need to create `child_numbers` -inside the loop but still in the main thread. +Let the compiler guide you. Also take a look at the book if you need help: +https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html""" -`child_numbers` should be a clone of the Arc of the numbers instead of a -thread-local copy of the numbers. +[[exercises]] +name = "lifetimes2" +path = "exercises/lifetimes/lifetimes2.rs" +mode = "compile" +hint = """ +What is the compiler checking? How could you change how long an owned variable lives?""" -This is a simple exercise if you understand the underlying concepts, but if this -is too much of a struggle, consider reading through all of Chapter 16 in the book: -https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html -""" +[[exercises]] +name = "lifetimes3" +path = "exercises/lifetimes/lifetimes3.rs" +mode = "compile" +hint = """ +If you use a lifetime annotation in a struct's fields, where else does it need to be added?""" + +# STANDARD LIBRARY TYPES [[exercises]] name = "iterators1" @@ -808,7 +860,8 @@ case is a vector of integers and the failure case is a DivisionError. The list_of_results function needs to return a vector of results. See https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect for how -the `FromIterator` trait is used in `collect()`.""" +the `FromIterator` trait is used in `collect()`. This trait is REALLY powerful! It +can make the solution to this exercise infinitely easier.""" [[exercises]] name = "iterators4" @@ -818,7 +871,9 @@ hint = """ In an imperative language, you might write a for loop that updates a mutable variable. Or, you might write code utilizing recursion and a match clause. In Rust you can take another functional -approach, computing the factorial elegantly with ranges and iterators.""" +approach, computing the factorial elegantly with ranges and iterators. + +Hint 2: Check out the `fold` and `rfold` methods!""" [[exercises]] name = "iterators5" @@ -839,6 +894,41 @@ The fold method can be useful in the count_collection_iterator function. For a further challenge, consult the documentation for Iterator to find a different method that could make your code more compact than using fold.""" +[[exercises]] +name = "box1" +path = "exercises/standard_library_types/box1.rs" +mode = "test" +hint = """ +Step 1 +The compiler's message should help: since we cannot store the value of the actual type +when working with recursive types, we need to store a reference (pointer) to its value. +We should, therefore, place our `List` inside a `Box`. More details in the book here: +https://doc.rust-lang.org/book/ch15-01-box.html#enabling-recursive-types-with-boxes + +Step 2 +Creating an empty list should be fairly straightforward (hint: peek at the assertions). +For a non-empty list keep in mind that we want to use our Cons "list builder". +Although the current list is one of integers (i32), feel free to change the definition +and try other types! +""" + +[[exercises]] +name = "arc1" +path = "exercises/standard_library_types/arc1.rs" +mode = "compile" +hint = """ +Make `shared_numbers` be an `Arc` from the numbers vector. Then, in order +to avoid creating a copy of `numbers`, you'll need to create `child_numbers` +inside the loop but still in the main thread. + +`child_numbers` should be a clone of the Arc of the numbers instead of a +thread-local copy of the numbers. + +This is a simple exercise if you understand the underlying concepts, but if this +is too much of a struggle, consider reading through all of Chapter 16 in the book: +https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html +""" + # THREADS [[exercises]] @@ -846,6 +936,22 @@ name = "threads1" path = "exercises/threads/threads1.rs" mode = "compile" hint = """ +`JoinHandle` is a struct that is returned from a spawned thread: +https://doc.rust-lang.org/std/thread/fn.spawn.html + +A challenge with multi-threaded applications is that the main thread can +finish before the spawned threads are completed. +https://doc.rust-lang.org/book/ch16-01-threads.html#waiting-for-all-threads-to-finish-using-join-handle + +Collect the JoinHandles and wait for them to finish. +https://doc.rust-lang.org/std/thread/struct.JoinHandle.html +""" + +[[exercises]] +name = "threads2" +path = "exercises/threads/threads2.rs" +mode = "compile" +hint = """ `Arc` is an Atomic Reference Counted pointer that allows safe, shared access to **immutable** data. But we want to *change* the number of `jobs_completed` so we'll need to also use another type that will only allow one thread to @@ -866,18 +972,24 @@ while they are sleeping, since this will prevent the other thread from being allowed to get the lock. Locks are automatically released when they go out of scope. -Ok, so, real talk, this was actually tricky for *me* to do too. And -I could see a lot of different problems you might run into, so at this -point I'm not sure which one you've hit :) - -Please open an issue if you're still running into a problem that -these hints are not helping you with, or if you've looked at the sample -answers and don't understand why they work and yours doesn't. - If you've learned from the sample solutions, I encourage you to come back to this exercise and try it again in a few days to reinforce what you've learned :)""" +[[exercises]] +name = "threads3" +path = "exercises/threads/threads3.rs" +mode = "compile" +hint = """ +An alternate way to handle concurrency between threads is to use +a mpsc (multiple producer, single consumer) channel to communicate. +With both a sending end and a receiving end, it's possible to +send values in one thread and receieve them in another. +Multiple producers are possibile by using clone() to create a duplicate +of the original sending end. +See https://doc.rust-lang.org/book/ch16-02-message-passing.html for more info. +""" + # MACROS [[exercises]] @@ -918,15 +1030,12 @@ mode = "compile" hint = """ You only need to add a single character to make this compile. The way macros are written, it wants to see something between each -"macro arm", so it can separate them.""" +"macro arm", so it can separate them. -# TEST 4 - -[[exercises]] -name = "quiz4" -path = "exercises/quiz4.rs" -mode = "test" -hint = "No hints this time ;)" +That's all the macro exercises we have in here, but it's barely even +scratching the surface of what you can do with Rust's macros. For a more +thorough introduction, you can have a read through the little book of Rust +macros: https://veykril.github.io/tlborm/""" # CLIPPY @@ -952,6 +1061,12 @@ mode = "clippy" hint = """ `for` loops over Option values are more clearly expressed as an `if let`""" +[[exercises]] +name = "clippy3" +path = "exercises/clippy/clippy3.rs" +mode = "clippy" +hint = "No hints this time!" + # TYPE CONVERSIONS [[exercises]] @@ -980,7 +1095,7 @@ or an Err with an error if the string is not valid. This is almost like the `from_into` exercise, but returning errors instead of falling back to a default value. -Hint: Look at the test cases to see which error variants to return. +Look at the test cases to see which error variants to return. Another hint: You can use the `map_err` method of `Result` with a function or a closure to wrap the error from `parse::`. @@ -998,7 +1113,7 @@ hint = """ Follow the steps provided right before the `TryFrom` implementation. You can also use the example at https://doc.rust-lang.org/std/convert/trait.TryFrom.html -Hint: Is there an implementation of `TryFrom` in the standard library that +Is there an implementation of `TryFrom` in the standard library that can both do the required integer conversion and check the range of the input? Another hint: Look at the test cases to see which error variants to return. @@ -1018,55 +1133,3 @@ path = "exercises/conversions/as_ref_mut.rs" mode = "test" hint = """ Add AsRef as a trait bound to the functions.""" - -# ADVANCED ERRORS - -[[exercises]] -name = "advanced_errs1" -path = "exercises/advanced_errors/advanced_errs1.rs" -mode = "test" -hint = """ -This exercise uses an updated version of the code in errors6. The parsing -code is now in an implementation of the `FromStr` trait. Note that the -parsing code uses `?` directly, without any calls to `map_err()`. There is -one partial implementation of the `From` trait example that you should -complete. - -Details: The `?` operator calls `From::from()` on the error type to convert -it to the error type of the return type of the surrounding function. - -Hint: You will need to write another implementation of `From` that has a -different input type. -""" - -[[exercises]] -name = "advanced_errs2" -path = "exercises/advanced_errors/advanced_errs2.rs" -mode = "test" -hint = """ -This exercise demonstrates a few traits that are useful for custom error -types to implement. These traits make it easier for other code to consume -the custom error type. - -Follow the steps in the comment near the top of the file. You will have to -supply a missing trait implementation, and complete a few incomplete ones. - -You may find these pages to be helpful references: -https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/define_error_type.html -https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/boxing_errors.html -https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/wrap_error.html - -Hint: What trait must our error type have for `main()` to return the return -type that it returns? - -Another hint: It's not necessary to implement any methods inside the missing -trait. (Some methods have default implementations that are supplied by the -trait.) - -Another hint: Consult the tests to determine which error variants (and which -error message text) to produce for certain error conditions. - -Challenge: There is one test that is marked `#[ignore]`. Can you supply the -missing code that will make it pass? You may want to consult the standard -library documentation for a certain trait for more hints. -""" diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 82c1e7e4..00000000 --- a/src/lib.rs +++ /dev/null @@ -1,3 +0,0 @@ -#[cfg(feature = "exercises")] -#[path = "../exercises/mod.rs"] -mod exercises;