From 9bdb0a12e45a8e9f9f6a4bd4a9c172c5376c7f60 Mon Sep 17 00:00:00 2001 From: marisa Date: Mon, 11 Nov 2019 16:51:38 +0100 Subject: [PATCH] feat: Refactor hint system Hints are now accessible using the CLI subcommand `rustlings hint Option { if name.len() > 0 { @@ -38,36 +38,3 @@ mod tests { ); } } - - - - - - - - - - - - - - - - - - - - -// `Err` is one of the variants of `Result`, so what the 2nd test is saying -// is that `generate_nametag_text` should return a `Result` instead of an -// `Option`. - -// To make this change, you'll need to: -// - update the return type in the function signature to be a Result that -// could be the variants `Ok(String)` and `Err(String)` -// - change the body of the function to return `Ok(stuff)` where it currently -// returns `Some(stuff)` -// - change the body of the function to return `Err(error message)` where it -// currently returns `None` -// - change the first test to expect `Ok(stuff)` where it currently expects -// `Some(stuff)`. diff --git a/exercises/error_handling/errors2.rs b/exercises/error_handling/errors2.rs index 8b81207e33..5ac63392ee 100644 --- a/exercises/error_handling/errors2.rs +++ b/exercises/error_handling/errors2.rs @@ -14,7 +14,7 @@ // and add. // There are at least two ways to implement this that are both correct-- but -// one is a lot shorter! Scroll down for hints to both ways. +// one is a lot shorter! Execute `rustlings hint errors2` for hints to both ways. use std::num::ParseIntError; @@ -43,27 +43,3 @@ mod tests { ); } } - - - - - - - - - - - - - - - - - -// One way to handle this is using a `match` statement on -// `item_quantity.parse::()` where the cases are `Ok(something)` and -// `Err(something)`. This pattern is very common in Rust, though, so there's -// a `?` operator that does pretty much what you would make that match statement -// do for you! Take a look at this section of the Error Handling chapter: -// https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator -// and give it a try! diff --git a/exercises/error_handling/errors3.rs b/exercises/error_handling/errors3.rs index 31800fcef7..d9ec133678 100644 --- a/exercises/error_handling/errors3.rs +++ b/exercises/error_handling/errors3.rs @@ -1,7 +1,8 @@ // errors3.rs // 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? Scroll for hints! +// Why not? What should we do to fix it? +// Execute `rustlings hint errors3` for hints! use std::num::ParseIntError; @@ -26,22 +27,3 @@ pub fn total_cost(item_quantity: &str) -> Result { Ok(qty * cost_per_item + processing_fee) } - - - - - - - - - - - - - - - - - - -// If other functions can return a `Result`, why shouldn't `main`? diff --git a/exercises/error_handling/errorsn.rs b/exercises/error_handling/errorsn.rs index c2b16ce36b..2f3566b420 100644 --- a/exercises/error_handling/errorsn.rs +++ b/exercises/error_handling/errorsn.rs @@ -13,7 +13,7 @@ // type goes where the question marks are, and how do we return // that type from the body of read_and_validate? // -// Scroll down for hints :) +// Execute `rustlings hint errors4` for hints :) use std::error; use std::fmt; @@ -110,138 +110,3 @@ impl error::Error for CreationError { } } } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// First hint: To figure out what type should go where the ??? is, take a look -// at the test helper function `test_with_str`, since it returns whatever -// `read_and_validate` returns and`test_with_str` has its signature fully -// specified. - - - - - - - - - - - - - - - - - - - - -// Next hint: There are three places in `read_and_validate` that we call a -// function that returns a `Result` (that is, the functions might fail). -// Apply the `?` operator on those calls so that we return immediately from -// `read_and_validate` if those function calls fail. - - - - - - - - - - - - - - - - - - - - -// 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 - - - - - - - - - - - - - - - - - - - - -// Another another hint: Note that because the `?` operator returns -// the *unwrapped* value in the `Ok` case, if we want to return a `Result` from -// `read_and_validate` for *its* success case, we'll have to rewrap a value -// that we got from the return value of a `?`ed call in an `Ok`-- this will -// look like `Ok(something)`. - - - - - - - - - - - - - - - - - - - - -// Another another another hint: `Result`s must be "used", that is, you'll -// get a warning if you don't handle a `Result` that you get in your -// function. Read more about that in the `std::result` module docs: -// https://doc.rust-lang.org/std/result/#results-must-be-used diff --git a/exercises/error_handling/option1.rs b/exercises/error_handling/option1.rs index c5a4a64d93..e334e93876 100644 --- a/exercises/error_handling/option1.rs +++ b/exercises/error_handling/option1.rs @@ -2,7 +2,7 @@ // This example panics because the second time it calls `pop`, the `vec` // is empty, so `pop` returns `None`, and `unwrap` panics if it's called // on `None`. Handle this in a more graceful way than calling `unwrap`! -// Scroll down for hints :) +// Execute `rustlings hint option1` for hints :) pub fn pop_too_much() -> bool { let mut list = vec![3]; @@ -27,31 +27,3 @@ mod tests { assert!(pop_too_much()); } } - - - - - - - - - - - - - - - - - - - - - - - -// Try using a `match` statement where the arms are `Some(thing)` and `None`. -// Or set a default value to print out if you get `None` by using the -// function `unwrap_or`. -// Or use an `if let` statement on the result of `pop()` to both destructure -// a `Some` value and only print out something if we have a value! diff --git a/exercises/error_handling/result1.rs b/exercises/error_handling/result1.rs index f9596e24bf..c3f2d6e337 100644 --- a/exercises/error_handling/result1.rs +++ b/exercises/error_handling/result1.rs @@ -1,5 +1,5 @@ // result1.rs -// Make this test pass! Scroll down for hints :) +// Make this test pass! Execute `rustlings hint option2` for hints :) #[derive(PartialEq, Debug)] struct PositiveNonzeroInteger(u64); @@ -25,22 +25,3 @@ fn test_creation() { ); assert_eq!(Err(CreationError::Zero), PositiveNonzeroInteger::new(0)); } - - - - - - - - - - - - - - - - -// `PositiveNonzeroInteger::new` is always creating a new instance and returning an `Ok` result. -// It should be doing some checking, returning an `Err` result if those checks fail, and only -// returning an `Ok` result if those checks determine that everything is... okay :) diff --git a/exercises/functions/functions1.rs b/exercises/functions/functions1.rs index 396dd56f99..313fe1eb00 100644 --- a/exercises/functions/functions1.rs +++ b/exercises/functions/functions1.rs @@ -1,44 +1,6 @@ // functions1.rs -// Make me compile! Scroll down for hints :) +// Make me compile! Execute `rustlings hint function1` for hints :) fn main() { call_me(); } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// This main function is calling a function that it expects to exist, but the -// function doesn't exist. It expects this function to have the name `call_me`. -// It expects this function to not take any arguments and not return a value. -// Sounds a lot like `main`, doesn't it? diff --git a/exercises/functions/functions2.rs b/exercises/functions/functions2.rs index 1cf95c3242..9bb3d76ebb 100644 --- a/exercises/functions/functions2.rs +++ b/exercises/functions/functions2.rs @@ -1,5 +1,5 @@ // functions2.rs -// Make me compile! Scroll down for hints :) +// Make me compile! Execute `rustlings hint functions2` for hints :) fn main() { call_me(3); @@ -10,33 +10,3 @@ fn call_me(num) { println!("Ring! Call number {}", i + 1); } } - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// Rust requires that all parts of a function's signature have type annotations, -// but `call_me` is missing the type annotation of `num`. diff --git a/exercises/functions/functions3.rs b/exercises/functions/functions3.rs index b17543b3e7..b2e90f6d9f 100644 --- a/exercises/functions/functions3.rs +++ b/exercises/functions/functions3.rs @@ -1,5 +1,5 @@ // functions3.rs -// Make me compile! Scroll down for hints :) +// Make me compile! Execute `rustlings hint functions3` for hints :) fn main() { call_me(); @@ -10,33 +10,3 @@ fn call_me(num: i32) { println!("Ring! Call number {}", i + 1); } } - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// This time, the function *declaration* is okay, but there's something wrong -// with the place where we're calling the function. diff --git a/exercises/functions/functions4.rs b/exercises/functions/functions4.rs index 5baca0e625..78fc27b725 100644 --- a/exercises/functions/functions4.rs +++ b/exercises/functions/functions4.rs @@ -1,5 +1,5 @@ // functions4.rs -// Make me compile! Scroll down for hints :) +// Make me compile! Execute `rustlings hint functions4` for hints :) // This store is having a sale where if the price is an even number, you get // 10 (money unit) off, but if it's an odd number, it's 3 (money unit) less. @@ -20,25 +20,3 @@ fn sale_price(price: i32) -> { fn is_even(num: i32) -> bool { num % 2 == 0 } - - - - - - - - - - - - - - - - - - - -// The error message points to line 12 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! diff --git a/exercises/functions/functions5.rs b/exercises/functions/functions5.rs index d9227c9e5e..c7841a6e11 100644 --- a/exercises/functions/functions5.rs +++ b/exercises/functions/functions5.rs @@ -1,5 +1,5 @@ // functions5.rs -// Make me compile! Scroll down for hints :) +// Make me compile! Execute `rustlings hint functions5` for hints :) fn main() { let answer = square(3); @@ -9,39 +9,3 @@ fn main() { fn square(num: i32) -> i32 { num * num; } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// This is a really common error that can be fixed by removing one character. -// It happens because Rust distinguishes between expressions and statements: expressions return -// a value based on its operand, and statements simply return a () type which behaves just like `void` in C/C++ language. -// We want to return a value of `i32` type from the `square` function, but it is returning a `()` type... -// They are not the same. There are two solutions: -// 1. Add a `return` ahead of `num * num;` -// 2. remove `;`, make it to be `num * num` diff --git a/exercises/if/if1.rs b/exercises/if/if1.rs index 6da09d379f..bce052afcf 100644 --- a/exercises/if/if1.rs +++ b/exercises/if/if1.rs @@ -5,7 +5,7 @@ pub fn bigger(a: i32, b: i32) -> i32 { // Do not use: // - another function call // - additional variables - // Scroll down for hints. + // Execute `rustlings hint if1` for hints } // Don't mind this for now :) @@ -23,36 +23,3 @@ mod tests { assert_eq!(42, bigger(32, 42)); } } - - - - - - - - - - - - - - - - - - - - - - - - - -// It's possible to do this in one line if you would like! -// Some similar examples from other languages: -// - In C(++) this would be: `a > b ? a : b` -// - In Python this would be: `a if a > b else b` -// Remember in Rust that: -// - the `if` condition does not need to be surrounded by parentheses -// - `if`/`else` conditionals are expressions -// - Each condition is followed by a `{}` block. diff --git a/exercises/macros/macros1.rs b/exercises/macros/macros1.rs index a7c78a57f4..0bc2a69e54 100644 --- a/exercises/macros/macros1.rs +++ b/exercises/macros/macros1.rs @@ -1,5 +1,5 @@ // macros1.rs -// Make me compile! Scroll down for hints :) +// Make me compile! Execute `rustlings hint macros1` for hints :) macro_rules! my_macro { () => { @@ -10,55 +10,3 @@ macro_rules! my_macro { fn main() { my_macro(); } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// When you call a macro, you need to add something special compared to a -// regular function call. If you're stuck, take a look at what's inside -// `my_macro`. diff --git a/exercises/macros/macros2.rs b/exercises/macros/macros2.rs index bc2e56be4b..c4b45101db 100644 --- a/exercises/macros/macros2.rs +++ b/exercises/macros/macros2.rs @@ -1,5 +1,5 @@ // macros2.rs -// Make me compile! Scroll down for hints :) +// Make me compile! Execute `rustlings hint macros2` for hints :) fn main() { my_macro!(); @@ -10,64 +10,3 @@ macro_rules! my_macro { println!("Check out my macro!"); }; } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// Macros don't quite play by the same rules as the rest of Rust, in terms of -// what's available where. - - - - - - - - -// Unlike other things in Rust, the order of "where you define a macro" versus -// "where you use it" actually matters. diff --git a/exercises/macros/macros3.rs b/exercises/macros/macros3.rs index 84c4308e23..9b08adc033 100644 --- a/exercises/macros/macros3.rs +++ b/exercises/macros/macros3.rs @@ -1,5 +1,6 @@ // macros3.rs -// Make me compile, without taking the macro out of the module! Scroll down for hints :) +// Make me compile, without taking the macro out of the module! +// Execute `rustlings hint macros3` for hints :) mod macros { macro_rules! my_macro { @@ -12,64 +13,3 @@ mod macros { fn main() { my_macro!(); } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// In order to use a macro outside of its module, you need to do something -// special to the module to lift the macro out into its parent. - - - - - - - - -// The same trick also works on "extern crate" statements for crates that have -// exported macros, if you've seen any of those around. diff --git a/exercises/macros/macros4.rs b/exercises/macros/macros4.rs index d844bb0d9f..2ecba7d062 100644 --- a/exercises/macros/macros4.rs +++ b/exercises/macros/macros4.rs @@ -1,5 +1,5 @@ // macros4.rs -// Make me compile! Scroll down for hints :) +// Make me compile! Execute `rustlings hint macros4` for hints :) macro_rules! my_macro { () => { @@ -14,64 +14,3 @@ fn main() { my_macro!(); my_macro!(7777); } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// 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. diff --git a/exercises/modules/modules1.rs b/exercises/modules/modules1.rs index 0e092c5fc7..c50d62f0b8 100644 --- a/exercises/modules/modules1.rs +++ b/exercises/modules/modules1.rs @@ -1,5 +1,5 @@ // modules1.rs -// Make me compile! Scroll down for hints :) +// Make me compile! Execute `rustlings hint modules1` for hints :) mod sausage_factory { fn make_sausage() { @@ -10,34 +10,3 @@ mod sausage_factory { fn main() { sausage_factory::make_sausage(); } - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// Everything is private in Rust by default-- but there's a keyword we can use -// to make something public! The compiler error should point to the thing that -// needs to be public. diff --git a/exercises/modules/modules2.rs b/exercises/modules/modules2.rs index 3cfa36db3c..c3bf4f79fd 100644 --- a/exercises/modules/modules2.rs +++ b/exercises/modules/modules2.rs @@ -1,5 +1,5 @@ // modules2.rs -// Make me compile! Scroll down for hints :) +// Make me compile! Execute `rustlings hint modules2` for hints :) mod delicious_snacks { use self::fruits::PEAR as fruit; @@ -23,25 +23,3 @@ fn main() { delicious_snacks::veggie ); } - - - - - - - - - - - - - - - - - -// The delicious_snacks module is trying to present an external -// interface (the `fruit` and `veggie` constants) that is different than -// its internal structure (the `fruits` and `veggies` modules and -// associated constants). It's almost there except for one keyword missing for -// each constant. diff --git a/exercises/move_semantics/move_semantics1.rs b/exercises/move_semantics/move_semantics1.rs index ab855fca38..a69ec6005e 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! Scroll down for hints :) +// Make me compile! Execute `rustlings hint move_semantics1` for hints :) fn main() { let vec0 = Vec::new(); @@ -22,21 +22,3 @@ fn fill_vec(vec: Vec) -> Vec { vec } - - - - - - - - - - - - - - - -// So you've got the "cannot borrow immutable local variable `vec1` as mutable" error on line 11, -// right? The fix for this is going to be adding one keyword, and the addition is NOT on line 11 -// where the error is. diff --git a/exercises/move_semantics/move_semantics2.rs b/exercises/move_semantics/move_semantics2.rs index f85b3ed63a..b945042273 100644 --- a/exercises/move_semantics/move_semantics2.rs +++ b/exercises/move_semantics/move_semantics2.rs @@ -1,5 +1,6 @@ // move_semantics2.rs -// Make me compile without changing line 10! Scroll down for hints :) +// Make me compile without changing line 10! +// Execute `rustlings hint move_semantics2` for hints :) fn main() { let vec0 = Vec::new(); @@ -23,31 +24,3 @@ fn fill_vec(vec: Vec) -> Vec { vec } - - - - - - - - - - - - - - -// So `vec0` is being *moved* into the function `fill_vec` when we call it on -// line 7, which means it gets dropped at the end of `fill_vec`, which means we -// can't use `vec0` again on line 10 (or anywhere else in `main` after the -// `fill_vec` call for that matter). We could fix this in a few ways, try them -// all! -// 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 -// 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!` diff --git a/exercises/move_semantics/move_semantics3.rs b/exercises/move_semantics/move_semantics3.rs index 8b918968f8..3f7958ffc4 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!) -// Scroll down for hints :) +// Execute `rustlings hint move_semantics3` for hints :) fn main() { let vec0 = Vec::new(); @@ -22,24 +22,3 @@ fn fill_vec(vec: Vec) -> Vec { vec } - - - - - - - - - - - - - - - - - -// The difference between this one and the previous ones is that the first line -// of `fn fill_vec` that had `let mut vec = vec;` is no longer there. You can, -// instead of adding that line back, add `mut` in one place that will change -// an existing binding to be a mutable binding instead of an immutable one :) diff --git a/exercises/move_semantics/move_semantics4.rs b/exercises/move_semantics/move_semantics4.rs index 90930f0f47..2143b81861 100644 --- a/exercises/move_semantics/move_semantics4.rs +++ b/exercises/move_semantics/move_semantics4.rs @@ -1,7 +1,8 @@ // move_semantics4.rs // Refactor this code so that instead of having `vec0` and creating the vector // in `fn main`, we instead create it within `fn fill_vec` and transfer the -// freshly created vector from fill_vec to its caller. Scroll for hints! +// freshly created vector from fill_vec to its caller. +// Execute `rustlings hint move_semantics4` for hints! fn main() { let vec0 = Vec::new(); @@ -25,24 +26,3 @@ fn fill_vec() -> Vec { vec } - - - - - - - - - - - - -// Stop reading whenever you feel like you have enough direction :) Or try -// doing one step and then fixing the compiler errors that result! -// So the end goal is to: -// - get rid of the first line in main that creates the new vector -// - so then `vec0` doesn't exist, so we can't pass it to `fill_vec` -// - we don't want to pass anything to `fill_vec`, so its signature should -// reflect that it does not take any arguments -// - since we're not creating a new vec in `main` anymore, we need to create -// a new vec in `fill_vec`, similarly to the way we did in `main` diff --git a/exercises/primitive_types/primitive_types3.rs b/exercises/primitive_types/primitive_types3.rs index 7ce2226277..e58c76c535 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. -// Scroll down for hints! +// Execute `rustlings hint primitive_types3` for hints! fn main() { let a = ??? @@ -11,37 +11,3 @@ fn main() { println!("Meh, I eat arrays like that for breakfast."); } } - - - - - - - - - - - - - - - - - - - - - - - - - - - -// 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`? diff --git a/exercises/primitive_types/primitive_types4.rs b/exercises/primitive_types/primitive_types4.rs index e1ccdbcc4b..2efa58a608 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 `if` statement -// returns true. Scroll down for hints!! +// returns true. Execute `rustlings hint primitive_types4` for hints!! #[test] fn main() { @@ -10,59 +10,3 @@ fn main() { assert_eq!([2, 3, 4], nice_slice) } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// 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 right hand of the `==` comparison does not -// have an ampersand for a reference since the left hand side is a -// reference, take a look at the Deref coercions section of the book: -// https://doc.rust-lang.org/book/ch15-02-deref.html diff --git a/exercises/primitive_types/primitive_types5.rs b/exercises/primitive_types/primitive_types5.rs index 0d53c9cb9d..6ab1c215b5 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. -// Scroll down for hints! +// Execute `rustlings hint primitive_types5` for hints! fn main() { let cat = ("Furry McFurson", 3.5); @@ -8,38 +8,3 @@ fn main() { println!("{} is {} years old.", name, age); } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// 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!! diff --git a/exercises/primitive_types/primitive_types6.rs b/exercises/primitive_types/primitive_types6.rs index 854544fe24..219a53ef2b 100644 --- a/exercises/primitive_types/primitive_types6.rs +++ b/exercises/primitive_types/primitive_types6.rs @@ -1,45 +1,9 @@ // primitive_types6.rs // Use a tuple index to access the second element of `numbers`. // You can put this right into the `println!` where the ??? is. -// Scroll down for hints! +// Execute `rustlings hint primitive_types6` for hints! fn main() { let numbers = (1, 2, 3); println!("The second number is {}", ???); } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// 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! diff --git a/exercises/standard_library_types/arc1.rs b/exercises/standard_library_types/arc1.rs index d610e5fc20..5aa02d9a8a 100644 --- a/exercises/standard_library_types/arc1.rs +++ b/exercises/standard_library_types/arc1.rs @@ -2,7 +2,7 @@ // Make this code compile by filling in a value for `shared_numbers` where the // TODO comment is and creating an initial binding for `child_numbers` // somewhere. Try not to create any copies of the `numbers` Vec! -// Scroll down for hints :) +// Execute `rustlings help arc1` for hints :) use std::sync::Arc; use std::thread; @@ -27,29 +27,3 @@ fn main() { handle.join().unwrap(); } } - - - - - - - - - - - - - - - - - - - - -// 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. diff --git a/exercises/standard_library_types/iterators2.rs b/exercises/standard_library_types/iterators2.rs index b6d336636c..e70d5b6664 100644 --- a/exercises/standard_library_types/iterators2.rs +++ b/exercises/standard_library_types/iterators2.rs @@ -3,7 +3,7 @@ // Step 1. Complete the `capitalize_first` function to pass the first two cases // Step 2. Apply the `capitalize_first` function to a vector of strings, ensuring that it returns a vector of strings as well // Step 3. Apply the `capitalize_first` function again to a list, but try and ensure it returns a single string -// As always, there are hints below! +// As always, there are hints if you execute `rustlings hint iterators2`! pub fn capitalize_first(input: &str) -> String { let mut c = input.chars(); @@ -44,102 +44,3 @@ mod tests { assert_eq!(capitalized_words, "Hello World"); } } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// Step 1 -// You need to call something on `first` before it can be collected -// Currently its type is `char`. Have a look at the methods that are available on that type: -// https://doc.rust-lang.org/std/primitive.char.html - - - - - - - - - - - - - - - - - - - - - - - - - - -// Step 2 -// First you'll need to turn the Vec into an iterator -// Then you'll need to apply your function unto each item in the vector -// P.s. Don't forget to collect() at the end! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// Step 3. -// This is very similar to the previous test. The only real change is that you will need to -// alter the type that collect is coerced into. For a bonus you could try doing this with a -// turbofish diff --git a/exercises/standard_library_types/iterators3.rs b/exercises/standard_library_types/iterators3.rs index c012795d57..d5a5afaae3 100644 --- a/exercises/standard_library_types/iterators3.rs +++ b/exercises/standard_library_types/iterators3.rs @@ -4,8 +4,7 @@ // 1. Complete the divide function to get the first four tests to pass // 2. Uncomment the last two tests and get them to pass by filling in // values for `x` using `division_results`. -// Scroll down for a minor hint for part 2, and scroll down further for -// a major hint. +// Execute `rustlings hint iterators3` to get some hints! // Have fun :-) #[derive(Debug, PartialEq, Eq)] @@ -75,72 +74,3 @@ mod tests { } */ } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// Minor hint: In each of the two cases in the match in main, you can create x with either -// a 'turbofish' or by hinting the type of x to the compiler. You may try both. - - - - - - - - - - - - - - - - - - - - - - - - - - - -// Major hint: Have a look at the Iter trait and at the explanation of its collect function. -// Especially the part about Result is interesting. diff --git a/exercises/standard_library_types/iterators4.rs b/exercises/standard_library_types/iterators4.rs index 13613a6c43..bcb7f3cb0f 100644 --- a/exercises/standard_library_types/iterators4.rs +++ b/exercises/standard_library_types/iterators4.rs @@ -9,7 +9,7 @@ pub fn factorial(num: u64) -> u64 { // - additional variables // For the most fun don't use: // - recursion - // Scroll down for hints. + // Execute `rustlings hint iterators4` for hints. } #[cfg(test)] @@ -30,32 +30,3 @@ mod tests { assert_eq!(24, factorial(4)); } } - - - - - - - - - - - - - - - - - - - - - - - - - -// In an imperative language you might write a for loop to iterate through -// multiply the values into a mutable variable. Or you might write code more -// functionally with recursion and a match clause. But you can also use ranges -// and iterators to solve this in rust. diff --git a/exercises/strings/strings1.rs b/exercises/strings/strings1.rs index 2e5088f90a..0faf86e6d9 100644 --- a/exercises/strings/strings1.rs +++ b/exercises/strings/strings1.rs @@ -1,5 +1,6 @@ // strings1.rs -// Make me compile without changing the function signature! Scroll down for hints :) +// Make me compile without changing the function signature! +// Execute `rustlings hint strings1` for hints ;) fn main() { let answer = current_favorite_color(); @@ -9,38 +10,3 @@ fn main() { fn current_favorite_color() -> String { "blue" } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// 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. diff --git a/exercises/strings/strings2.rs b/exercises/strings/strings2.rs index c77e16fe81..bd0f35cc24 100644 --- a/exercises/strings/strings2.rs +++ b/exercises/strings/strings2.rs @@ -1,5 +1,6 @@ // strings2.rs -// Make me compile without changing the function signature! Scroll down for hints :) +// Make me compile without changing the function signature! +// Execute `rustlings hint strings2` for hints :) fn main() { let word = String::from("green"); // Try not changing this line :) @@ -13,32 +14,3 @@ fn main() { fn is_a_color_word(attempt: &str) -> bool { attempt == "green" || attempt == "blue" || attempt == "red" } - - - - - - - - - - - - - - - - - - - - - - - - - - -// 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 -// 6, though, that will coerce the `String` into a string slice. diff --git a/exercises/tests/tests1.rs b/exercises/tests/tests1.rs index b11221fd17..5eb918a788 100644 --- a/exercises/tests/tests1.rs +++ b/exercises/tests/tests1.rs @@ -4,7 +4,7 @@ // rustlings run --test exercises/tests/tests1.rs // This test has a problem with it -- make the test compile! Make the test -// pass! Make the test fail! Scroll down for hints :) +// pass! Make the test fail! Execute `rustlings hint tests1` for hints :) #[cfg(test)] mod tests { @@ -13,37 +13,3 @@ mod tests { assert!(); } } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// You don't even need to write any code to test -- you can just test values and run that, even -// though you wouldn't do that in real life :) `assert!` is a macro that needs an argument. -// Depending on the value of the argument, `assert!` will do nothing (in which case the test will -// pass) or `assert!` will panic (in which case the test will fail). So try giving different values -// to `assert!` and see which ones compile, which ones pass, and which ones fail :) diff --git a/exercises/tests/tests2.rs b/exercises/tests/tests2.rs index 6775d61041..044ed9ec3e 100644 --- a/exercises/tests/tests2.rs +++ b/exercises/tests/tests2.rs @@ -1,6 +1,6 @@ // tests2.rs // This test has a problem with it -- make the test compile! Make the test -// pass! Make the test fail! Scroll down for hints :) +// pass! Make the test fail! Execute `rustlings hint tests2` for hints :) #[cfg(test)] mod tests { @@ -9,36 +9,3 @@ mod tests { assert_eq!(); } } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// Like the previous exercise, you don't need to write any code to get this test to compile and -// run. `assert_eq!` is a macro that takes two arguments and compares them. Try giving it two -// values that are equal! Try giving it two arguments that are different! Try giving it two values -// that are of different types! Try switching which argument comes first and which comes second! diff --git a/exercises/tests/tests3.rs b/exercises/tests/tests3.rs index e10d2aaa32..0f244b3ecf 100644 --- a/exercises/tests/tests3.rs +++ b/exercises/tests/tests3.rs @@ -1,7 +1,8 @@ // tests3.rs // 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)`. Scroll down for hints! +// we expect to get when we call `is_even(5)`. +// Execute `rustlings hint tests3` for hints :) pub fn is_even(num: i32) -> bool { num % 2 == 0 @@ -16,28 +17,3 @@ mod tests { assert!(); } } - - - - - - - - - - - - - - - - - - - - - - -// You can call a function right where you're passing arguments to `assert!` -- so you could do -// 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())`. diff --git a/exercises/threads/threads1.rs b/exercises/threads/threads1.rs index 7983668ae9..330832130d 100644 --- a/exercises/threads/threads1.rs +++ b/exercises/threads/threads1.rs @@ -1,8 +1,8 @@ // threads1.rs -// Make this compile! Scroll down for hints :) The idea is the thread -// spawned on line 19 is completing jobs while the main thread is +// Make this compile! Execute `rustlings hint threads1` for hints :) +// The idea is the thread spawned on line 19 is completing jobs while the main thread is // monitoring progress until 10 jobs are completed. If you see 6 lines -// of "waiting..." and the program ends without timing out the playground, +// of "waiting..." and the program ends without timing out when running, // you've got it :) use std::sync::Arc; @@ -27,69 +27,3 @@ fn main() { thread::sleep(Duration::from_millis(500)); } } - - - - - - - - - - - - - - -// `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 -// mutate the data at a time. Take a look at this section of the book: -// https://doc.rust-lang.org/book/ch16-03-shared-state.html#atomic-reference-counting-with-arct -// and keep scrolling if you'd like more hints :) - - - - - - - - - - -// Do you now have an `Arc` `Mutex` `JobStatus` at the beginning of main? Like: -// `let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 }));` -// Similar to the code in the example in the book that happens after the text -// that says "We can use Arc to fix this.". If not, give that a try! If you -// do and would like more hints, keep scrolling!! - - - - - - - - - - - - -// Make sure neither of your threads are holding onto the lock of the mutex -// 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 see a few possible -// answers on https://github.com/carols10cents/rustlings/issues/3 -- -// mine is a little more complicated because I decided I wanted to see -// the number of jobs currently done when I was checking the status. - -// 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 :) diff --git a/exercises/variables/variables1.rs b/exercises/variables/variables1.rs index 1cdd270271..65452c207b 100644 --- a/exercises/variables/variables1.rs +++ b/exercises/variables/variables1.rs @@ -1,42 +1,7 @@ // variables1.rs -// Make me compile! Scroll down for hints :) +// Make me compile! Execute the command `rustlings hint variables1` if you want a hint :) fn main() { x = 5; println!("x has the value {}", x); } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// Hint: The declaration on line 5 is missing a keyword that is needed in Rust -// to create a new variable binding. diff --git a/exercises/variables/variables2.rs b/exercises/variables/variables2.rs index a0b4a37665..388b05d67e 100644 --- a/exercises/variables/variables2.rs +++ b/exercises/variables/variables2.rs @@ -1,5 +1,5 @@ // variables2.rs -// Make me compile! Scroll down for hints :) +// Make me compile! Execute the command `rustlings hint variables2` if you want a hint :) fn main() { let x; @@ -9,39 +9,3 @@ fn main() { println!("Not ten!"); } } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// The compiler message is saying that Rust cannot infer the type that the -// variable binding `x` has with what is given here. -// What happens if you annotate line 5 with a type annotation? -// What if you give x a value? -// What if you do both? -// What type should x be, anyway? -// What if x is the same type as 10? What if it's a different type? diff --git a/exercises/variables/variables3.rs b/exercises/variables/variables3.rs index 165a277951..f2c9c63541 100644 --- a/exercises/variables/variables3.rs +++ b/exercises/variables/variables3.rs @@ -1,5 +1,5 @@ // variables3.rs -// Make me compile! Scroll down for hints :) +// Make me compile! Execute the command `rustlings hint variables3` if you want a hint :) fn main() { let x = 3; @@ -7,37 +7,3 @@ fn main() { x = 5; println!("Number {}", x); } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// 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. diff --git a/exercises/variables/variables4.rs b/exercises/variables/variables4.rs index 71ebf0f09b..f1ed6ef16b 100644 --- a/exercises/variables/variables4.rs +++ b/exercises/variables/variables4.rs @@ -1,45 +1,7 @@ // variables4.rs -// Make me compile! Scroll down for hints :) +// Make me compile! Execute the command `rustlings hint variables4` if you want a hint :) fn main() { let x: i32; println!("Number {}", x); } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// Oops! In this exercise, we have a variable binding that we've created on -// line 5, and we're trying to use it on line 6, 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! diff --git a/info.toml b/info.toml index 96f27fabf0..5293cb4071 100644 --- a/info.toml +++ b/info.toml @@ -4,21 +4,42 @@ name = "variables1" path = "exercises/variables/variables1.rs" mode = "compile" +hint = """ +Hint: The declaration on line 5 is missing a keyword that is needed in Rust +to create a new variable binding.""" [[exercises]] name = "variables2" path = "exercises/variables/variables2.rs" mode = "compile" +hint = """ +The compiler message is saying that Rust cannot infer the type that the +variable binding `x` has with what is given here. +What happens if you annotate line 5 with a type annotation? +What if you give x a value? +What if you do both? +What type should x be, anyway? +What if x is the same type as 10? What if it's a different type?""" [[exercises]] 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.""" [[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 5, and we're trying to use it on line 6, 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!""" # IF @@ -26,6 +47,15 @@ mode = "compile" name = "if1" path = "exercises/if/if1.rs" mode = "test" +hint = """ +It's possible to do this in one line if you would like! +Some similar examples from other languages: +- In C(++) this would be: `a > b ? a : b` +- In Python this would be: `a if a > b else b` +Remember in Rust that: +- the `if` condition does not need to be surrounded by parentheses +- `if`/`else` conditionals are expressions +- Each condition is followed by a `{}` block.""" # FUNCTIONS @@ -33,26 +63,50 @@ mode = "test" name = "functions1" path = "exercises/functions/functions1.rs" mode = "compile" +hint = """ +This main function is calling a function that it expects to exist, but the +function doesn't exist. It expects this function to have the name `call_me`. +It expects this function to not take any arguments and not return a value. +Sounds a lot like `main`, doesn't it?""" [[exercises]] name = "functions2" path = "exercises/functions/functions2.rs" mode = "compile" +hint = """ +Rust requires that all parts of a function's signature have type annotations, +but `call_me` is missing the type annotation of `num`.""" [[exercises]] name = "functions3" 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.""" [[exercises]] name = "functions4" path = "exercises/functions/functions4.rs" mode = "compile" +hint = """ +The error message points to line 12 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!""" [[exercises]] name = "functions5" path = "exercises/functions/functions5.rs" mode = "compile" +hint = """ +This is a really common error that can be fixed by removing one character. +It happens because Rust distinguishes between expressions and statements: expressions return +a value based on its operand, and statements simply return a () type which behaves just like `void` in C/C++ language. +We want to return a value of `i32` type from the `square` function, but it is returning a `()` type... +They are not the same. There are two solutions: +1. Add a `return` ahead of `num * num;` +2. remove `;`, make it to be `num * num` +""" # TEST 1 @@ -60,6 +114,7 @@ mode = "compile" name = "test1" path = "exercises/test1.rs" mode = "test" +hint = "No hints this time ;)" # PRIMITIVE TYPES @@ -67,31 +122,64 @@ mode = "test" 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 right hand of the `==` comparison does not +have an ampersand for a reference since the left hand side 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 = "compile" +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 @@ -99,11 +187,13 @@ mode = "compile" name = "structs1" path = "exercises/structs/structs1.rs" mode = "test" +hint = "No hints this time ;)" [[exercises]] name = "structs2" path = "exercises/structs/structs2.rs" mode = "test" +hint = "No hints this time ;)" # STRINGS @@ -111,11 +201,22 @@ mode = "test" 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 +6, though, that will coerce the `String` into a string slice.""" # TEST 2 @@ -123,6 +224,7 @@ mode = "compile" name = "test2" path = "exercises/test2.rs" mode = "compile" +hint = "No hints this time ;)" # ENUMS @@ -130,16 +232,22 @@ mode = "compile" name = "enums1" path = "exercises/enums/enums1.rs" mode = "compile" +hint = """ +Hint: The declaration of the enumeration type has not been defined yet.""" [[exercises]] name = "enums2" path = "exercises/enums/enums2.rs" mode = "compile" +hint = """ +Hint: 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 = "No hints this time ;)" # TESTS @@ -147,16 +255,31 @@ mode = "test" name = "tests1" path = "exercises/tests/tests1.rs" mode = "test" +hint = """ +You don't even need to write any code to test -- you can just test values and run that, even +though you wouldn't do that in real life :) `assert!` is a macro that needs an argument. +Depending on the value of the argument, `assert!` will do nothing (in which case the test will +pass) or `assert!` will panic (in which case the test will fail). So try giving different values +to `assert!` and see which ones compile, which ones pass, and which ones fail :)""" [[exercises]] name = "tests2" path = "exercises/tests/tests2.rs" mode = "test" +hint = """ +Like the previous exercise, you don't need to write any code to get this test to compile and +run. `assert_eq!` is a macro that takes two arguments and compares them. Try giving it two +values that are equal! Try giving it two arguments that are different! Try giving it two values +that are of different types! Try switching which argument comes first and which comes second!""" [[exercises]] name = "tests3" path = "exercises/tests/tests3.rs" mode = "test" +hint = """ +You can call a function right where you're passing arguments to `assert!` -- so you could do +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 @@ -164,6 +287,7 @@ mode = "test" name = "test3" path = "exercises/test3.rs" mode = "test" +hint = "No hints this time ;)" # MODULES @@ -171,11 +295,21 @@ mode = "test" name = "modules1" path = "exercises/modules/modules1.rs" mode = "compile" +hint = """ +Everything is private in Rust by default-- but there's a keyword we can use +to make something public! The compiler error should point to the thing that +needs to be public.""" [[exercises]] name = "modules2" path = "exercises/modules/modules2.rs" mode = "compile" +hint = """ +The delicious_snacks module is trying to present an external +interface (the `fruit` and `veggie` constants) that is different than +its internal structure (the `fruits` and `veggies` modules and +associated constants). It's almost there except for one keyword missing for +each constant.""" # MACROS @@ -183,28 +317,48 @@ mode = "compile" name = "macros1" path = "exercises/macros/macros1.rs" mode = "compile" +hint = """ +When you call a macro, you need to add something special compared to a +regular function call. If you're stuck, take a look at what's inside +`my_macro`.""" [[exercises]] name = "macros2" path = "exercises/macros/macros2.rs" mode = "compile" +hint = """ +Macros don't quite play by the same rules as the rest of Rust, in terms of +what's available where. + +Unlike other things in Rust, the order of "where you define a macro" versus +"where you use it" actually matters.""" [[exercises]] name = "macros3" path = "exercises/macros/macros3.rs" mode = "compile" +hint = """ +In order to use a macro outside of its module, you need to do something +special to the module to lift the macro out into its parent. + +The same trick also works on "extern crate" statements for crates that have +exported macros, if you've seen any of those around.""" [[exercises]] name = "macros4" path = "exercises/macros/macros4.rs" 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.""" # TEST 4 [[exercises]] name = "test4" path = "exercises/test4.rs" mode = "compile" +hint = "No hints this time ;)" # MOVE SEMANTICS @@ -212,21 +366,55 @@ mode = "compile" name = "move_semantics1" path = "exercises/move_semantics/move_semantics1.rs" mode = "compile" +hint = """ +So you've got the "cannot borrow immutable local variable `vec1` as mutable" error on line 11, +right? The fix for this is going to be adding one keyword, and the addition is NOT on line 11 +where the error is.""" [[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 7, which means it gets dropped at the end of `fill_vec`, which means we +can't use `vec0` again on line 10 (or anywhere else in `main` after the +`fill_vec` call for that matter). We could fix this in a few ways, try them +all! +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 + 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!`""" [[exercises]] name = "move_semantics3" path = "exercises/move_semantics/move_semantics3.rs" mode = "compile" +hint = """ +The difference between this one and the previous ones is that the first line +of `fn fill_vec` that had `let mut vec = vec;` is no longer there. You can, +instead of adding that line back, add `mut` in one place that will change +an existing binding to be a mutable binding instead of an immutable one :)""" [[exercises]] name = "move_semantics4" path = "exercises/move_semantics/move_semantics4.rs" mode = "compile" +hint = """ +Stop reading whenever you feel like you have enough direction :) Or try +doing one step and then fixing the compiler errors that result! +So the end goal is to: + - get rid of the first line in main that creates the new vector + - so then `vec0` doesn't exist, so we can't pass it to `fill_vec` + - we don't want to pass anything to `fill_vec`, so its signature should + reflect that it does not take any arguments + - since we're not creating a new vec in `main` anymore, we need to create + a new vec in `fill_vec`, similarly to the way we did in `main`""" # ERROR HANDLING @@ -234,21 +422,78 @@ mode = "compile" name = "errors1" path = "exercises/error_handling/errors1.rs" mode = "test" +hint = """ +`Err` is one of the variants of `Result`, so what the 2nd test is saying +is that `generate_nametag_text` should return a `Result` instead of an +`Option`. + +To make this change, you'll need to: + - update the return type in the function signature to be a Result that + could be the variants `Ok(String)` and `Err(String)` + - change the body of the function to return `Ok(stuff)` where it currently + returns `Some(stuff)` + - change the body of the function to return `Err(error message)` where it + currently returns `None` + - change the first test to expect `Ok(stuff)` where it currently expects + `Some(stuff)`.""" [[exercises]] name = "errors2" path = "exercises/error_handling/errors2.rs" mode = "test" +hint = """ +One way to handle this is using a `match` statement on +`item_quantity.parse::()` where the cases are `Ok(something)` and +`Err(something)`. This pattern is very common in Rust, though, so there's +a `?` operator that does pretty much what you would make that match statement +do for you! Take a look at this section of the Error Handling chapter: +https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator +and give it a try!""" [[exercises]] name = "errors3" path = "exercises/error_handling/errors3.rs" mode = "test" +hint = """ +If other functions can return a `Result`, why shouldn't `main`?""" [[exercises]] name = "errors4" path = "exercises/error_handling/errorsn.rs" mode = "test" +hint = """ +First hint: To figure out what type should go where the ??? is, take a look +at the test helper function `test_with_str`, since it returns whatever +`read_and_validate` returns and`test_with_str` has its signature fully +specified. + + +Next hint: There are three places in `read_and_validate` that we call a +function that returns a `Result` (that is, the functions might fail). +Apply the `?` operator on those calls so that we return immediately from +`read_and_validate` if those function calls fail. + + +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 + + +Another another hint: Note that because the `?` operator returns +the *unwrapped* value in the `Ok` case, if we want to return a `Result` from +`read_and_validate` for *its* success case, we'll have to rewrap a value +that we got from the return value of a `?`ed call in an `Ok`-- this will +look like `Ok(something)`. + + +Another another another hint: `Result`s must be "used", that is, you'll +get a warning if you don't handle a `Result` that you get in your +function. Read more about that in the `std::result` module docs: +https://doc.rust-lang.org/std/result/#results-must-be-used""" # OPTIONS / RESULTS @@ -256,11 +501,21 @@ mode = "test" name = "option1" path = "exercises/error_handling/option1.rs" mode = "test" +hint = """ +Try using a `match` statement where the arms are `Some(thing)` and `None`. +Or set a default value to print out if you get `None` by using the +function `unwrap_or`. +Or use an `if let` statement on the result of `pop()` to both destructure +a `Some` value and only print out something if we have a value!""" [[exercises]] name = "option2" path = "exercises/error_handling/result1.rs" mode = "test" +hint = """ +`PositiveNonzeroInteger::new` is always creating a new instance and returning an `Ok` result. +It should be doing some checking, returning an `Err` result if those checks fail, and only +returning an `Ok` result if those checks determine that everything is... okay :)""" # STANDARD LIBRARY TYPES @@ -268,21 +523,56 @@ mode = "test" 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.""" [[exercises]] name = "iterators2" path = "exercises/standard_library_types/iterators2.rs" mode = "test" +hint = """ +Step 1 +You need to call something on `first` before it can be collected +Currently its type is `char`. Have a look at the methods that are available on that type: +https://doc.rust-lang.org/std/primitive.char.html + + +Step 2 +First you'll need to turn the Vec into an iterator +Then you'll need to apply your function unto each item in the vector +P.s. Don't forget to collect() at the end! + + +Step 3. +This is very similar to the previous test. The only real change is that you will need to +alter the type that collect is coerced into. For a bonus you could try doing this with a +turbofish""" [[exercises]] name = "iterators3" path = "exercises/standard_library_types/iterators3.rs" mode = "test" +hint = """ +Minor hint: In each of the two cases in the match in main, you can create x with either +a 'turbofish' or by hinting the type of x to the compiler. You may try both. + +Major hint: Have a look at the Iter trait and at the explanation of its collect function. +Especially the part about Result is interesting.""" [[exercises]] name = "iterators4" path = "exercises/standard_library_types/iterators4.rs" mode = "test" +hint = """ +In an imperative language you might write a for loop to iterate through +multiply the values into a mutable variable. Or you might write code more +functionally with recursion and a match clause. But you can also use ranges +and iterators to solve this in rust.""" # THREADS @@ -290,3 +580,35 @@ mode = "test" name = "threads1" path = "exercises/threads/threads1.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 +mutate the data at a time. Take a look at this section of the book: +https://doc.rust-lang.org/book/ch16-03-shared-state.html#atomic-reference-counting-with-arct +and keep reading if you'd like more hints :) + + +Do you now have an `Arc` `Mutex` `JobStatus` at the beginning of main? Like: +`let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 }));` +Similar to the code in the example in the book that happens after the text +that says "We can use Arc to fix this.". If not, give that a try! If you +do and would like more hints, keep reading!! + + +Make sure neither of your threads are holding onto the lock of the mutex +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 :)""" diff --git a/src/exercise.rs b/src/exercise.rs index 9cd88a001a..0e8a199052 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -27,6 +27,7 @@ pub struct Exercise { pub name: String, pub path: PathBuf, pub mode: Mode, + pub hint: String, } impl Exercise { @@ -74,6 +75,7 @@ mod test { name: String::from("example"), path: PathBuf::from("example.rs"), mode: Mode::Test, + hint: String::from(""), }; exercise.clean(); assert!(!Path::new(&temp_file()).exists()); diff --git a/src/main.rs b/src/main.rs index 100186b665..69a7b22b41 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,6 +27,12 @@ fn main() { .about("Runs/Tests a single exercise") .arg(Arg::with_name("name").required(true).index(1)), ) + .subcommand( + SubCommand::with_name("hint") + .alias("h") + .about("Returns a hint for the current exercise") + .arg(Arg::with_name("name").required(true).index(1)), + ) .get_matches(); if None == matches.subcommand_name() { @@ -71,6 +77,20 @@ fn main() { run(&exercise).unwrap_or_else(|_| std::process::exit(1)); } + if let Some(ref matches) = matches.subcommand_matches("hint") { + let name = matches.value_of("name").unwrap_or_else(|| { + println!("Please supply an exercise name!"); + std::process::exit(1); + }); + + let exercise = exercises.iter().find(|e| name == e.name).unwrap_or_else(|| { + println!("No exercise found for your given name!"); + std::process::exit(1) + }); + + println!("{}", exercise.hint); + } + if matches.subcommand_matches("verify").is_some() { verify(&exercises).unwrap_or_else(|_| std::process::exit(1)); } diff --git a/tests/fixture/failure/info.toml b/tests/fixture/failure/info.toml index 19e2bda29d..20d570a12d 100644 --- a/tests/fixture/failure/info.toml +++ b/tests/fixture/failure/info.toml @@ -2,8 +2,10 @@ name = "compFailure" path = "compFailure.rs" mode = "compile" +hint = """""" [[exercises]] name = "testFailure" path = "testFailure.rs" mode = "test" +hint = """""" diff --git a/tests/fixture/success/info.toml b/tests/fixture/success/info.toml index c6316c136e..68d35388ee 100644 --- a/tests/fixture/success/info.toml +++ b/tests/fixture/success/info.toml @@ -2,8 +2,10 @@ name = "compSuccess" path = "compSuccess.rs" mode = "compile" +hint = """""" [[exercises]] name = "testSuccess" path = "testSuccess.rs" mode = "test" +hint = """"""