From 627cdc07d07dfe6a740e885e0ddf6900e7ec336b Mon Sep 17 00:00:00 2001 From: marisa Date: Mon, 11 Nov 2019 15:46:32 +0100 Subject: [PATCH 1/6] feat: Index exercises by name BREAKING CHANGE: This changes the way you use `rustlings run` by now requiring an abridged form of the previous filename, e.g: `rustlings run exercises/if/if1.rs` becomes `rustlings run if1` --- info.toml | 51 +++++++++++++++++++++++++++++++++ src/exercise.rs | 2 ++ src/main.rs | 14 ++++----- tests/fixture/failure/info.toml | 4 ++- tests/fixture/success/info.toml | 4 ++- tests/integration_tests.rs | 8 +++--- 6 files changed, 68 insertions(+), 15 deletions(-) diff --git a/info.toml b/info.toml index d908c9eb..96f27fab 100644 --- a/info.toml +++ b/info.toml @@ -1,241 +1,292 @@ # VARIABLES [[exercises]] +name = "variables1" path = "exercises/variables/variables1.rs" mode = "compile" [[exercises]] +name = "variables2" path = "exercises/variables/variables2.rs" mode = "compile" [[exercises]] +name = "variables3" path = "exercises/variables/variables3.rs" mode = "compile" [[exercises]] +name = "variables4" path = "exercises/variables/variables4.rs" mode = "compile" # IF [[exercises]] +name = "if1" path = "exercises/if/if1.rs" mode = "test" # FUNCTIONS [[exercises]] +name = "functions1" path = "exercises/functions/functions1.rs" mode = "compile" [[exercises]] +name = "functions2" path = "exercises/functions/functions2.rs" mode = "compile" [[exercises]] +name = "functions3" path = "exercises/functions/functions3.rs" mode = "compile" [[exercises]] +name = "functions4" path = "exercises/functions/functions4.rs" mode = "compile" [[exercises]] +name = "functions5" path = "exercises/functions/functions5.rs" mode = "compile" # TEST 1 [[exercises]] +name = "test1" path = "exercises/test1.rs" mode = "test" # PRIMITIVE TYPES [[exercises]] +name = "primitive_types1" path = "exercises/primitive_types/primitive_types1.rs" mode = "compile" [[exercises]] +name = "primitive_types2" path = "exercises/primitive_types/primitive_types2.rs" mode = "compile" [[exercises]] +name = "primitive_types3" path = "exercises/primitive_types/primitive_types3.rs" mode = "compile" [[exercises]] +name = "primitive_types4" path = "exercises/primitive_types/primitive_types4.rs" mode = "test" [[exercises]] +name = "primitive_types5" path = "exercises/primitive_types/primitive_types5.rs" mode = "compile" [[exercises]] +name = "primitive_types6" path = "exercises/primitive_types/primitive_types6.rs" mode = "compile" # STRUCTS [[exercises]] +name = "structs1" path = "exercises/structs/structs1.rs" mode = "test" [[exercises]] +name = "structs2" path = "exercises/structs/structs2.rs" mode = "test" # STRINGS [[exercises]] +name = "strings1" path = "exercises/strings/strings1.rs" mode = "compile" [[exercises]] +name = "strings2" path = "exercises/strings/strings2.rs" mode = "compile" # TEST 2 [[exercises]] +name = "test2" path = "exercises/test2.rs" mode = "compile" # ENUMS [[exercises]] +name = "enums1" path = "exercises/enums/enums1.rs" mode = "compile" [[exercises]] +name = "enums2" path = "exercises/enums/enums2.rs" mode = "compile" [[exercises]] +name = "enums3" path = "exercises/enums/enums3.rs" mode = "test" # TESTS [[exercises]] +name = "tests1" path = "exercises/tests/tests1.rs" mode = "test" [[exercises]] +name = "tests2" path = "exercises/tests/tests2.rs" mode = "test" [[exercises]] +name = "tests3" path = "exercises/tests/tests3.rs" mode = "test" # TEST 3 [[exercises]] +name = "test3" path = "exercises/test3.rs" mode = "test" # MODULES [[exercises]] +name = "modules1" path = "exercises/modules/modules1.rs" mode = "compile" [[exercises]] +name = "modules2" path = "exercises/modules/modules2.rs" mode = "compile" # MACROS [[exercises]] +name = "macros1" path = "exercises/macros/macros1.rs" mode = "compile" [[exercises]] +name = "macros2" path = "exercises/macros/macros2.rs" mode = "compile" [[exercises]] +name = "macros3" path = "exercises/macros/macros3.rs" mode = "compile" [[exercises]] +name = "macros4" path = "exercises/macros/macros4.rs" mode = "compile" # TEST 4 [[exercises]] +name = "test4" path = "exercises/test4.rs" mode = "compile" # MOVE SEMANTICS [[exercises]] +name = "move_semantics1" path = "exercises/move_semantics/move_semantics1.rs" mode = "compile" [[exercises]] +name = "move_semantics2" path = "exercises/move_semantics/move_semantics2.rs" mode = "compile" [[exercises]] +name = "move_semantics3" path = "exercises/move_semantics/move_semantics3.rs" mode = "compile" [[exercises]] +name = "move_semantics4" path = "exercises/move_semantics/move_semantics4.rs" mode = "compile" # ERROR HANDLING [[exercises]] +name = "errors1" path = "exercises/error_handling/errors1.rs" mode = "test" [[exercises]] +name = "errors2" path = "exercises/error_handling/errors2.rs" mode = "test" [[exercises]] +name = "errors3" path = "exercises/error_handling/errors3.rs" mode = "test" [[exercises]] +name = "errors4" path = "exercises/error_handling/errorsn.rs" mode = "test" # OPTIONS / RESULTS [[exercises]] +name = "option1" path = "exercises/error_handling/option1.rs" mode = "test" [[exercises]] +name = "option2" path = "exercises/error_handling/result1.rs" mode = "test" # STANDARD LIBRARY TYPES [[exercises]] +name = "arc1" path = "exercises/standard_library_types/arc1.rs" mode = "compile" [[exercises]] +name = "iterators2" path = "exercises/standard_library_types/iterators2.rs" mode = "test" [[exercises]] +name = "iterators3" path = "exercises/standard_library_types/iterators3.rs" mode = "test" [[exercises]] +name = "iterators4" path = "exercises/standard_library_types/iterators4.rs" mode = "test" # THREADS [[exercises]] +name = "threads1" path = "exercises/threads/threads1.rs" mode = "compile" diff --git a/src/exercise.rs b/src/exercise.rs index 6f526e74..9cd88a00 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -24,6 +24,7 @@ pub struct ExerciseList { #[derive(Deserialize)] pub struct Exercise { + pub name: String, pub path: PathBuf, pub mode: Mode, } @@ -70,6 +71,7 @@ mod test { fn test_clean() { File::create(&temp_file()).unwrap(); let exercise = Exercise { + name: String::from("example"), path: PathBuf::from("example.rs"), mode: Mode::Test, }; diff --git a/src/main.rs b/src/main.rs index aad4cfff..100186b6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,8 +25,7 @@ fn main() { SubCommand::with_name("run") .alias("r") .about("Runs/Tests a single exercise") - .arg(Arg::with_name("file").required(true).index(1)) - .arg(Arg::with_name("test").short("t").long("test").help("Run the file as a test")), + .arg(Arg::with_name("name").required(true).index(1)), ) .get_matches(); @@ -55,20 +54,17 @@ fn main() { let exercises = toml::from_str::(toml_str).unwrap().exercises; if let Some(ref matches) = matches.subcommand_matches("run") { - let filename = matches.value_of("file").unwrap_or_else(|| { - println!("Please supply a file name!"); + let name = matches.value_of("name").unwrap_or_else(|| { + println!("Please supply an exercise name!"); std::process::exit(1); }); let matching_exercise = |e: &&Exercise| { - Path::new(filename) - .canonicalize() - .map(|p| p.ends_with(&e.path)) - .unwrap_or(false) + name == e.name }; let exercise = exercises.iter().find(matching_exercise).unwrap_or_else(|| { - println!("No exercise found for your file name!"); + println!("No exercise found for your given name!"); std::process::exit(1) }); diff --git a/tests/fixture/failure/info.toml b/tests/fixture/failure/info.toml index f4e7c0cb..19e2bda2 100644 --- a/tests/fixture/failure/info.toml +++ b/tests/fixture/failure/info.toml @@ -1,7 +1,9 @@ [[exercises]] +name = "compFailure" path = "compFailure.rs" mode = "compile" [[exercises]] +name = "testFailure" path = "testFailure.rs" -mode = "test" \ No newline at end of file +mode = "test" diff --git a/tests/fixture/success/info.toml b/tests/fixture/success/info.toml index e255db9f..c6316c13 100644 --- a/tests/fixture/success/info.toml +++ b/tests/fixture/success/info.toml @@ -1,7 +1,9 @@ [[exercises]] +name = "compSuccess" path = "compSuccess.rs" mode = "compile" [[exercises]] +name = "testSuccess" path = "testSuccess.rs" -mode = "test" \ No newline at end of file +mode = "test" diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 3acaf908..652ba605 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -40,7 +40,7 @@ fn verify_all_failure() { fn run_single_compile_success() { Command::cargo_bin("rustlings") .unwrap() - .args(&["r", "compSuccess.rs"]) + .args(&["r", "compSuccess"]) .current_dir("tests/fixture/success/") .assert() .success(); @@ -50,7 +50,7 @@ fn run_single_compile_success() { fn run_single_compile_failure() { Command::cargo_bin("rustlings") .unwrap() - .args(&["r", "compFailure.rs"]) + .args(&["r", "compFailure"]) .current_dir("tests/fixture/failure/") .assert() .code(1); @@ -60,7 +60,7 @@ fn run_single_compile_failure() { fn run_single_test_success() { Command::cargo_bin("rustlings") .unwrap() - .args(&["r", "testSuccess.rs"]) + .args(&["r", "testSuccess"]) .current_dir("tests/fixture/success/") .assert() .success(); @@ -70,7 +70,7 @@ fn run_single_test_success() { fn run_single_test_failure() { Command::cargo_bin("rustlings") .unwrap() - .args(&["r", "testFailure.rs"]) + .args(&["r", "testFailure"]) .current_dir("tests/fixture/failure/") .assert() .code(1); From 9bdb0a12e45a8e9f9f6a4bd4a9c172c5376c7f60 Mon Sep 17 00:00:00 2001 From: marisa Date: Mon, 11 Nov 2019 16:51:38 +0100 Subject: [PATCH 2/6] 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 8b81207e..5ac63392 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 31800fce..d9ec1336 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 c2b16ce3..2f3566b4 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 c5a4a64d..e334e938 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 f9596e24..c3f2d6e3 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 396dd56f..313fe1eb 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 1cf95c32..9bb3d76e 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 b17543b3..b2e90f6d 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 5baca0e6..78fc27b7 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 d9227c9e..c7841a6e 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 6da09d37..bce052af 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 a7c78a57..0bc2a69e 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 bc2e56be..c4b45101 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 84c4308e..9b08adc0 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 d844bb0d..2ecba7d0 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 0e092c5f..c50d62f0 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 3cfa36db..c3bf4f79 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 ab855fca..a69ec600 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 f85b3ed6..b9450422 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 8b918968..3f7958ff 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 90930f0f..2143b818 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 7ce22262..e58c76c5 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 e1ccdbcc..2efa58a6 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 0d53c9cb..6ab1c215 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 854544fe..219a53ef 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 d610e5fc..5aa02d9a 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 b6d33663..e70d5b66 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 c012795d..d5a5afaa 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 13613a6c..bcb7f3cb 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 2e5088f9..0faf86e6 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 c77e16fe..bd0f35cc 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 b11221fd..5eb918a7 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 6775d610..044ed9ec 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 e10d2aaa..0f244b3e 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 7983668a..33083213 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 1cdd2702..65452c20 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 a0b4a376..388b05d6 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 165a2779..f2c9c635 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 71ebf0f0..f1ed6ef1 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 96f27fab..5293cb40 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 9cd88a00..0e8a1990 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 100186b6..69a7b22b 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 19e2bda2..20d570a1 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 c6316c13..68d35388 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 = """""" From 795b6e348094a898e9227a14f6232f7bb94c8d31 Mon Sep 17 00:00:00 2001 From: marisa Date: Mon, 11 Nov 2019 16:56:39 +0100 Subject: [PATCH 3/6] fix(info): Fix trailing newlines for hints --- info.toml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/info.toml b/info.toml index 5293cb40..84d58217 100644 --- a/info.toml +++ b/info.toml @@ -105,8 +105,7 @@ a value based on its operand, and statements simply return a () type which behav 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` -""" +2. remove `;`, make it to be `num * num`""" # TEST 1 @@ -156,8 +155,7 @@ 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 -""" +https://doc.rust-lang.org/book/ch15-02-deref.html""" [[exercises]] name = "primitive_types5" From ce9fa6ebbfdc3e7585d488d9409797285708316f Mon Sep 17 00:00:00 2001 From: marisa Date: Mon, 11 Nov 2019 17:19:50 +0100 Subject: [PATCH 4/6] feat(hint): Add test for hint --- src/main.rs | 15 ++++++++------- tests/fixture/failure/info.toml | 4 ++-- tests/integration_tests.rs | 11 +++++++++++ 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/main.rs b/src/main.rs index 69a7b22b..875f7675 100644 --- a/src/main.rs +++ b/src/main.rs @@ -65,9 +65,7 @@ fn main() { std::process::exit(1); }); - let matching_exercise = |e: &&Exercise| { - name == e.name - }; + let matching_exercise = |e: &&Exercise| name == e.name; let exercise = exercises.iter().find(matching_exercise).unwrap_or_else(|| { println!("No exercise found for your given name!"); @@ -83,10 +81,13 @@ fn main() { 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) - }); + 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); } diff --git a/tests/fixture/failure/info.toml b/tests/fixture/failure/info.toml index 20d570a1..e5949f9b 100644 --- a/tests/fixture/failure/info.toml +++ b/tests/fixture/failure/info.toml @@ -2,10 +2,10 @@ name = "compFailure" path = "compFailure.rs" mode = "compile" -hint = """""" +hint = "" [[exercises]] name = "testFailure" path = "testFailure.rs" mode = "test" -hint = """""" +hint = "Hello!" diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 652ba605..24268ec5 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -105,3 +105,14 @@ fn run_single_test_no_exercise() { .assert() .code(1); } + +#[test] +fn get_hint_for_single_test() { + Command::cargo_bin("rustlings") + .unwrap() + .args(&["h", "testFailure"]) + .current_dir("tests/fixture/failure") + .assert() + .code(0) + .stdout("Hello!\n"); +} From 48c35bcfbc251bcfc8d8a2abaa141e260209c285 Mon Sep 17 00:00:00 2001 From: marisa Date: Mon, 11 Nov 2019 17:28:19 +0100 Subject: [PATCH 5/6] fix tests --- src/exercise.rs | 4 ++++ tests/integration_tests.rs | 1 + 2 files changed, 5 insertions(+) diff --git a/src/exercise.rs b/src/exercise.rs index be59284f..b6c28da4 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -142,8 +142,10 @@ mod test { #[test] fn test_pending_state() { let exercise = Exercise { + name: "pending_exercise".into(), path: PathBuf::from("tests/fixture/state/pending_exercise.rs"), mode: Mode::Compile, + hint: String::new(), }; let state = exercise.state(); @@ -181,8 +183,10 @@ mod test { #[test] fn test_finished_exercise() { let exercise = Exercise { + name: "finished_exercise".into(), path: PathBuf::from("tests/fixture/state/finished_exercise.rs"), mode: Mode::Compile, + hint: String::new(), }; assert_eq!(exercise.state(), State::Done); diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 32f4341f..754aa635 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -118,6 +118,7 @@ fn get_hint_for_single_test() { .assert() .code(0) .stdout("Hello!\n"); +} #[test] fn all_exercises_require_confirmation() { From 1a7bb5a4005e66665d2618b20bea132f009b79f9 Mon Sep 17 00:00:00 2001 From: marisa Date: Mon, 11 Nov 2019 17:47:45 +0100 Subject: [PATCH 6/6] Address feedback --- exercises/error_handling/errorsn.rs | 2 +- exercises/error_handling/result1.rs | 2 +- exercises/functions/functions1.rs | 2 +- info.toml | 4 ++-- src/main.rs | 10 ++-------- 5 files changed, 7 insertions(+), 13 deletions(-) diff --git a/exercises/error_handling/errorsn.rs b/exercises/error_handling/errorsn.rs index fc25308c..30799430 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? // -// Execute `rustlings hint errors4` for hints :) +// Execute `rustlings hint errorsn` for hints :) // I AM NOT DONE diff --git a/exercises/error_handling/result1.rs b/exercises/error_handling/result1.rs index 352a6c25..b978001b 100644 --- a/exercises/error_handling/result1.rs +++ b/exercises/error_handling/result1.rs @@ -1,5 +1,5 @@ // result1.rs -// Make this test pass! Execute `rustlings hint option2` for hints :) +// Make this test pass! Execute `rustlings hint result1` for hints :) // I AM NOT DONE diff --git a/exercises/functions/functions1.rs b/exercises/functions/functions1.rs index 49d48e93..31125278 100644 --- a/exercises/functions/functions1.rs +++ b/exercises/functions/functions1.rs @@ -1,5 +1,5 @@ // functions1.rs -// Make me compile! Execute `rustlings hint function1` for hints :) +// Make me compile! Execute `rustlings hint functions1` for hints :) // I AM NOT DONE diff --git a/info.toml b/info.toml index 84d58217..c7ad1e2d 100644 --- a/info.toml +++ b/info.toml @@ -456,7 +456,7 @@ hint = """ If other functions can return a `Result`, why shouldn't `main`?""" [[exercises]] -name = "errors4" +name = "errorsn" path = "exercises/error_handling/errorsn.rs" mode = "test" hint = """ @@ -507,7 +507,7 @@ 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" +name = "result1" path = "exercises/error_handling/result1.rs" mode = "test" hint = """ diff --git a/src/main.rs b/src/main.rs index 5a4af539..d1d0d6d7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -60,10 +60,7 @@ fn main() { let exercises = toml::from_str::(toml_str).unwrap().exercises; if let Some(ref matches) = matches.subcommand_matches("run") { - let name = matches.value_of("name").unwrap_or_else(|| { - println!("Please supply an exercise name!"); - std::process::exit(1); - }); + let name = matches.value_of("name").unwrap(); let matching_exercise = |e: &&Exercise| name == e.name; @@ -76,10 +73,7 @@ fn main() { } 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 name = matches.value_of("name").unwrap(); let exercise = exercises .iter()