feat: Refactor hint system
Hints are now accessible using the CLI subcommand `rustlings hint <exercise name`. BREAKING CHANGE: This fundamentally changes the way people interact with exercises.
This commit is contained in:
parent
627cdc07d0
commit
9bdb0a12e4
|
@ -1,5 +1,5 @@
|
||||||
// enums1.rs
|
// enums1.rs
|
||||||
// Make me compile! Scroll down for hints!
|
// Make me compile! Execute `rustlings hint enums1` for hints!
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum Message {
|
enum Message {
|
||||||
|
@ -12,31 +12,3 @@ fn main() {
|
||||||
println!("{:?}", Message::Move);
|
println!("{:?}", Message::Move);
|
||||||
println!("{:?}", Message::ChangeColor);
|
println!("{:?}", Message::ChangeColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Hint: The declaration of the enumeration type has not been defined yet.
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// enums2.rs
|
// enums2.rs
|
||||||
// Make me compile! Scroll down for hints
|
// Make me compile! Execute `rustlings hint enums2` for hints!
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum Message {
|
enum Message {
|
||||||
|
@ -24,38 +24,3 @@ fn main() {
|
||||||
message.call();
|
message.call();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Hint: you can create enumerations that have different variants with different types
|
|
||||||
// such as no data, anonymous structs, a single string, tuples, ...etc
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
// was, instead of just sometimes returning `None`. The 2nd test currently
|
// was, instead of just sometimes returning `None`. The 2nd test currently
|
||||||
// does not compile or pass, but it illustrates the behavior we would like
|
// does not compile or pass, but it illustrates the behavior we would like
|
||||||
// this function to have.
|
// this function to have.
|
||||||
// Scroll down for hints!!!
|
// Execute `rustlings hint errors1` for hints!
|
||||||
|
|
||||||
pub fn generate_nametag_text(name: String) -> Option<String> {
|
pub fn generate_nametag_text(name: String) -> Option<String> {
|
||||||
if name.len() > 0 {
|
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<String, String> 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)`.
|
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
// and add.
|
// and add.
|
||||||
|
|
||||||
// There are at least two ways to implement this that are both correct-- but
|
// 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;
|
use std::num::ParseIntError;
|
||||||
|
|
||||||
|
@ -43,27 +43,3 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// One way to handle this is using a `match` statement on
|
|
||||||
// `item_quantity.parse::<i32>()` 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!
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
// errors3.rs
|
// errors3.rs
|
||||||
// This is a program that is trying to use a completed version of the
|
// 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!
|
// `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;
|
use std::num::ParseIntError;
|
||||||
|
|
||||||
|
@ -26,22 +27,3 @@ pub fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
|
||||||
|
|
||||||
Ok(qty * cost_per_item + processing_fee)
|
Ok(qty * cost_per_item + processing_fee)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// If other functions can return a `Result`, why shouldn't `main`?
|
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
// type goes where the question marks are, and how do we return
|
// type goes where the question marks are, and how do we return
|
||||||
// that type from the body of read_and_validate?
|
// that type from the body of read_and_validate?
|
||||||
//
|
//
|
||||||
// Scroll down for hints :)
|
// Execute `rustlings hint errors4` for hints :)
|
||||||
|
|
||||||
use std::error;
|
use std::error;
|
||||||
use std::fmt;
|
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<dyn error::Error>,
|
|
||||||
// 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
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// This example panics because the second time it calls `pop`, the `vec`
|
// 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
|
// 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`!
|
// 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 {
|
pub fn pop_too_much() -> bool {
|
||||||
let mut list = vec![3];
|
let mut list = vec![3];
|
||||||
|
@ -27,31 +27,3 @@ mod tests {
|
||||||
assert!(pop_too_much());
|
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!
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// result1.rs
|
// result1.rs
|
||||||
// Make this test pass! Scroll down for hints :)
|
// Make this test pass! Execute `rustlings hint option2` for hints :)
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
struct PositiveNonzeroInteger(u64);
|
struct PositiveNonzeroInteger(u64);
|
||||||
|
@ -25,22 +25,3 @@ fn test_creation() {
|
||||||
);
|
);
|
||||||
assert_eq!(Err(CreationError::Zero), PositiveNonzeroInteger::new(0));
|
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 :)
|
|
||||||
|
|
|
@ -1,44 +1,6 @@
|
||||||
// functions1.rs
|
// functions1.rs
|
||||||
// Make me compile! Scroll down for hints :)
|
// Make me compile! Execute `rustlings hint function1` for hints :)
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
call_me();
|
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?
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// functions2.rs
|
// functions2.rs
|
||||||
// Make me compile! Scroll down for hints :)
|
// Make me compile! Execute `rustlings hint functions2` for hints :)
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
call_me(3);
|
call_me(3);
|
||||||
|
@ -10,33 +10,3 @@ fn call_me(num) {
|
||||||
println!("Ring! Call number {}", i + 1);
|
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`.
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// functions3.rs
|
// functions3.rs
|
||||||
// Make me compile! Scroll down for hints :)
|
// Make me compile! Execute `rustlings hint functions3` for hints :)
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
call_me();
|
call_me();
|
||||||
|
@ -10,33 +10,3 @@ fn call_me(num: i32) {
|
||||||
println!("Ring! Call number {}", i + 1);
|
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.
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// functions4.rs
|
// 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
|
// 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.
|
// 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 {
|
fn is_even(num: i32) -> bool {
|
||||||
num % 2 == 0
|
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!
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// functions5.rs
|
// functions5.rs
|
||||||
// Make me compile! Scroll down for hints :)
|
// Make me compile! Execute `rustlings hint functions5` for hints :)
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let answer = square(3);
|
let answer = square(3);
|
||||||
|
@ -9,39 +9,3 @@ fn main() {
|
||||||
fn square(num: i32) -> i32 {
|
fn square(num: i32) -> i32 {
|
||||||
num * num;
|
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`
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ pub fn bigger(a: i32, b: i32) -> i32 {
|
||||||
// Do not use:
|
// Do not use:
|
||||||
// - another function call
|
// - another function call
|
||||||
// - additional variables
|
// - additional variables
|
||||||
// Scroll down for hints.
|
// Execute `rustlings hint if1` for hints
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't mind this for now :)
|
// Don't mind this for now :)
|
||||||
|
@ -23,36 +23,3 @@ mod tests {
|
||||||
assert_eq!(42, bigger(32, 42));
|
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.
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// macros1.rs
|
// macros1.rs
|
||||||
// Make me compile! Scroll down for hints :)
|
// Make me compile! Execute `rustlings hint macros1` for hints :)
|
||||||
|
|
||||||
macro_rules! my_macro {
|
macro_rules! my_macro {
|
||||||
() => {
|
() => {
|
||||||
|
@ -10,55 +10,3 @@ macro_rules! my_macro {
|
||||||
fn main() {
|
fn main() {
|
||||||
my_macro();
|
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`.
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// macros2.rs
|
// macros2.rs
|
||||||
// Make me compile! Scroll down for hints :)
|
// Make me compile! Execute `rustlings hint macros2` for hints :)
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
my_macro!();
|
my_macro!();
|
||||||
|
@ -10,64 +10,3 @@ macro_rules! my_macro {
|
||||||
println!("Check out 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.
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
// macros3.rs
|
// 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 {
|
mod macros {
|
||||||
macro_rules! my_macro {
|
macro_rules! my_macro {
|
||||||
|
@ -12,64 +13,3 @@ mod macros {
|
||||||
fn main() {
|
fn main() {
|
||||||
my_macro!();
|
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.
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// macros4.rs
|
// macros4.rs
|
||||||
// Make me compile! Scroll down for hints :)
|
// Make me compile! Execute `rustlings hint macros4` for hints :)
|
||||||
|
|
||||||
macro_rules! my_macro {
|
macro_rules! my_macro {
|
||||||
() => {
|
() => {
|
||||||
|
@ -14,64 +14,3 @@ fn main() {
|
||||||
my_macro!();
|
my_macro!();
|
||||||
my_macro!(7777);
|
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.
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// modules1.rs
|
// modules1.rs
|
||||||
// Make me compile! Scroll down for hints :)
|
// Make me compile! Execute `rustlings hint modules1` for hints :)
|
||||||
|
|
||||||
mod sausage_factory {
|
mod sausage_factory {
|
||||||
fn make_sausage() {
|
fn make_sausage() {
|
||||||
|
@ -10,34 +10,3 @@ mod sausage_factory {
|
||||||
fn main() {
|
fn main() {
|
||||||
sausage_factory::make_sausage();
|
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.
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// modules2.rs
|
// modules2.rs
|
||||||
// Make me compile! Scroll down for hints :)
|
// Make me compile! Execute `rustlings hint modules2` for hints :)
|
||||||
|
|
||||||
mod delicious_snacks {
|
mod delicious_snacks {
|
||||||
use self::fruits::PEAR as fruit;
|
use self::fruits::PEAR as fruit;
|
||||||
|
@ -23,25 +23,3 @@ fn main() {
|
||||||
delicious_snacks::veggie
|
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.
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// move_semantics1.rs
|
// move_semantics1.rs
|
||||||
// Make me compile! Scroll down for hints :)
|
// Make me compile! Execute `rustlings hint move_semantics1` for hints :)
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let vec0 = Vec::new();
|
let vec0 = Vec::new();
|
||||||
|
@ -22,21 +22,3 @@ fn fill_vec(vec: Vec<i32>) -> Vec<i32> {
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
// move_semantics2.rs
|
// 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() {
|
fn main() {
|
||||||
let vec0 = Vec::new();
|
let vec0 = Vec::new();
|
||||||
|
@ -23,31 +24,3 @@ fn fill_vec(vec: Vec<i32>) -> Vec<i32> {
|
||||||
|
|
||||||
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<i32>`
|
|
||||||
// 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!`
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// move_semantics3.rs
|
// move_semantics3.rs
|
||||||
// Make me compile without adding new lines-- just changing existing lines!
|
// Make me compile without adding new lines-- just changing existing lines!
|
||||||
// (no lines with multiple semicolons necessary!)
|
// (no lines with multiple semicolons necessary!)
|
||||||
// Scroll down for hints :)
|
// Execute `rustlings hint move_semantics3` for hints :)
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let vec0 = Vec::new();
|
let vec0 = Vec::new();
|
||||||
|
@ -22,24 +22,3 @@ fn fill_vec(vec: Vec<i32>) -> Vec<i32> {
|
||||||
|
|
||||||
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 :)
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
// move_semantics4.rs
|
// move_semantics4.rs
|
||||||
// Refactor this code so that instead of having `vec0` and creating the vector
|
// 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
|
// 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() {
|
fn main() {
|
||||||
let vec0 = Vec::new();
|
let vec0 = Vec::new();
|
||||||
|
@ -25,24 +26,3 @@ fn fill_vec() -> Vec<i32> {
|
||||||
|
|
||||||
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`
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// primitive_types3.rs
|
// primitive_types3.rs
|
||||||
// Create an array with at least 100 elements in it where the ??? is.
|
// 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() {
|
fn main() {
|
||||||
let a = ???
|
let a = ???
|
||||||
|
@ -11,37 +11,3 @@ fn main() {
|
||||||
println!("Meh, I eat arrays like that for breakfast.");
|
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`?
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// primitive_types4.rs
|
// primitive_types4.rs
|
||||||
// Get a slice out of Array a where the ??? is so that the `if` statement
|
// 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]
|
#[test]
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -10,59 +10,3 @@ fn main() {
|
||||||
|
|
||||||
assert_eq!([2, 3, 4], nice_slice)
|
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
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// primitive_types5.rs
|
// primitive_types5.rs
|
||||||
// Destructure the `cat` tuple so that the println will work.
|
// Destructure the `cat` tuple so that the println will work.
|
||||||
// Scroll down for hints!
|
// Execute `rustlings hint primitive_types5` for hints!
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let cat = ("Furry McFurson", 3.5);
|
let cat = ("Furry McFurson", 3.5);
|
||||||
|
@ -8,38 +8,3 @@ fn main() {
|
||||||
|
|
||||||
println!("{} is {} years old.", name, age);
|
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!!
|
|
||||||
|
|
|
@ -1,45 +1,9 @@
|
||||||
// primitive_types6.rs
|
// primitive_types6.rs
|
||||||
// Use a tuple index to access the second element of `numbers`.
|
// Use a tuple index to access the second element of `numbers`.
|
||||||
// You can put this right into the `println!` where the ??? is.
|
// You can put this right into the `println!` where the ??? is.
|
||||||
// Scroll down for hints!
|
// Execute `rustlings hint primitive_types6` for hints!
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let numbers = (1, 2, 3);
|
let numbers = (1, 2, 3);
|
||||||
println!("The second number is {}", ???);
|
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!
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// Make this code compile by filling in a value for `shared_numbers` where the
|
// 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`
|
// TODO comment is and creating an initial binding for `child_numbers`
|
||||||
// somewhere. Try not to create any copies of the `numbers` Vec!
|
// 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::sync::Arc;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
@ -27,29 +27,3 @@ fn main() {
|
||||||
handle.join().unwrap();
|
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.
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
// Step 1. Complete the `capitalize_first` function to pass the first two cases
|
// 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 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
|
// 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 {
|
pub fn capitalize_first(input: &str) -> String {
|
||||||
let mut c = input.chars();
|
let mut c = input.chars();
|
||||||
|
@ -44,102 +44,3 @@ mod tests {
|
||||||
assert_eq!(capitalized_words, "Hello World");
|
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
|
|
||||||
|
|
|
@ -4,8 +4,7 @@
|
||||||
// 1. Complete the divide function to get the first four tests to pass
|
// 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
|
// 2. Uncomment the last two tests and get them to pass by filling in
|
||||||
// values for `x` using `division_results`.
|
// values for `x` using `division_results`.
|
||||||
// Scroll down for a minor hint for part 2, and scroll down further for
|
// Execute `rustlings hint iterators3` to get some hints!
|
||||||
// a major hint.
|
|
||||||
// Have fun :-)
|
// Have fun :-)
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[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.
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ pub fn factorial(num: u64) -> u64 {
|
||||||
// - additional variables
|
// - additional variables
|
||||||
// For the most fun don't use:
|
// For the most fun don't use:
|
||||||
// - recursion
|
// - recursion
|
||||||
// Scroll down for hints.
|
// Execute `rustlings hint iterators4` for hints.
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -30,32 +30,3 @@ mod tests {
|
||||||
assert_eq!(24, factorial(4));
|
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.
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
// strings1.rs
|
// 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() {
|
fn main() {
|
||||||
let answer = current_favorite_color();
|
let answer = current_favorite_color();
|
||||||
|
@ -9,38 +10,3 @@ fn main() {
|
||||||
fn current_favorite_color() -> String {
|
fn current_favorite_color() -> String {
|
||||||
"blue"
|
"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.
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
// strings2.rs
|
// 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() {
|
fn main() {
|
||||||
let word = String::from("green"); // Try not changing this line :)
|
let word = String::from("green"); // Try not changing this line :)
|
||||||
|
@ -13,32 +14,3 @@ fn main() {
|
||||||
fn is_a_color_word(attempt: &str) -> bool {
|
fn is_a_color_word(attempt: &str) -> bool {
|
||||||
attempt == "green" || attempt == "blue" || attempt == "red"
|
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.
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
// rustlings run --test exercises/tests/tests1.rs
|
// rustlings run --test exercises/tests/tests1.rs
|
||||||
|
|
||||||
// This test has a problem with it -- make the test compile! Make the test
|
// 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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
@ -13,37 +13,3 @@ mod tests {
|
||||||
assert!();
|
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 :)
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// tests2.rs
|
// tests2.rs
|
||||||
// This test has a problem with it -- make the test compile! Make the test
|
// 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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
@ -9,36 +9,3 @@ mod tests {
|
||||||
assert_eq!();
|
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!
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
// tests3.rs
|
// tests3.rs
|
||||||
// This test isn't testing our function -- make it do that in such a way that
|
// 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
|
// 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 {
|
pub fn is_even(num: i32) -> bool {
|
||||||
num % 2 == 0
|
num % 2 == 0
|
||||||
|
@ -16,28 +17,3 @@ mod tests {
|
||||||
assert!();
|
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())`.
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
// threads1.rs
|
// threads1.rs
|
||||||
// Make this compile! Scroll down for hints :) The idea is the thread
|
// Make this compile! Execute `rustlings hint threads1` for hints :)
|
||||||
// spawned on line 19 is completing jobs while the main thread is
|
// 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
|
// 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 :)
|
// you've got it :)
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -27,69 +27,3 @@ fn main() {
|
||||||
thread::sleep(Duration::from_millis(500));
|
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<T> 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 :)
|
|
||||||
|
|
|
@ -1,42 +1,7 @@
|
||||||
// variables1.rs
|
// 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() {
|
fn main() {
|
||||||
x = 5;
|
x = 5;
|
||||||
println!("x has the value {}", x);
|
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.
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// variables2.rs
|
// 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() {
|
fn main() {
|
||||||
let x;
|
let x;
|
||||||
|
@ -9,39 +9,3 @@ fn main() {
|
||||||
println!("Not ten!");
|
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?
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// variables3.rs
|
// 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() {
|
fn main() {
|
||||||
let x = 3;
|
let x = 3;
|
||||||
|
@ -7,37 +7,3 @@ fn main() {
|
||||||
x = 5;
|
x = 5;
|
||||||
println!("Number {}", x);
|
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.
|
|
||||||
|
|
|
@ -1,45 +1,7 @@
|
||||||
// variables4.rs
|
// 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() {
|
fn main() {
|
||||||
let x: i32;
|
let x: i32;
|
||||||
println!("Number {}", x);
|
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!
|
|
||||||
|
|
324
info.toml
324
info.toml
|
@ -4,21 +4,42 @@
|
||||||
name = "variables1"
|
name = "variables1"
|
||||||
path = "exercises/variables/variables1.rs"
|
path = "exercises/variables/variables1.rs"
|
||||||
mode = "compile"
|
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]]
|
[[exercises]]
|
||||||
name = "variables2"
|
name = "variables2"
|
||||||
path = "exercises/variables/variables2.rs"
|
path = "exercises/variables/variables2.rs"
|
||||||
mode = "compile"
|
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]]
|
[[exercises]]
|
||||||
name = "variables3"
|
name = "variables3"
|
||||||
path = "exercises/variables/variables3.rs"
|
path = "exercises/variables/variables3.rs"
|
||||||
mode = "compile"
|
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]]
|
[[exercises]]
|
||||||
name = "variables4"
|
name = "variables4"
|
||||||
path = "exercises/variables/variables4.rs"
|
path = "exercises/variables/variables4.rs"
|
||||||
mode = "compile"
|
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
|
# IF
|
||||||
|
|
||||||
|
@ -26,6 +47,15 @@ mode = "compile"
|
||||||
name = "if1"
|
name = "if1"
|
||||||
path = "exercises/if/if1.rs"
|
path = "exercises/if/if1.rs"
|
||||||
mode = "test"
|
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
|
# FUNCTIONS
|
||||||
|
|
||||||
|
@ -33,26 +63,50 @@ mode = "test"
|
||||||
name = "functions1"
|
name = "functions1"
|
||||||
path = "exercises/functions/functions1.rs"
|
path = "exercises/functions/functions1.rs"
|
||||||
mode = "compile"
|
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]]
|
[[exercises]]
|
||||||
name = "functions2"
|
name = "functions2"
|
||||||
path = "exercises/functions/functions2.rs"
|
path = "exercises/functions/functions2.rs"
|
||||||
mode = "compile"
|
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]]
|
[[exercises]]
|
||||||
name = "functions3"
|
name = "functions3"
|
||||||
path = "exercises/functions/functions3.rs"
|
path = "exercises/functions/functions3.rs"
|
||||||
mode = "compile"
|
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]]
|
[[exercises]]
|
||||||
name = "functions4"
|
name = "functions4"
|
||||||
path = "exercises/functions/functions4.rs"
|
path = "exercises/functions/functions4.rs"
|
||||||
mode = "compile"
|
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]]
|
[[exercises]]
|
||||||
name = "functions5"
|
name = "functions5"
|
||||||
path = "exercises/functions/functions5.rs"
|
path = "exercises/functions/functions5.rs"
|
||||||
mode = "compile"
|
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
|
# TEST 1
|
||||||
|
|
||||||
|
@ -60,6 +114,7 @@ mode = "compile"
|
||||||
name = "test1"
|
name = "test1"
|
||||||
path = "exercises/test1.rs"
|
path = "exercises/test1.rs"
|
||||||
mode = "test"
|
mode = "test"
|
||||||
|
hint = "No hints this time ;)"
|
||||||
|
|
||||||
# PRIMITIVE TYPES
|
# PRIMITIVE TYPES
|
||||||
|
|
||||||
|
@ -67,31 +122,64 @@ mode = "test"
|
||||||
name = "primitive_types1"
|
name = "primitive_types1"
|
||||||
path = "exercises/primitive_types/primitive_types1.rs"
|
path = "exercises/primitive_types/primitive_types1.rs"
|
||||||
mode = "compile"
|
mode = "compile"
|
||||||
|
hint = "No hints this time ;)"
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
name = "primitive_types2"
|
name = "primitive_types2"
|
||||||
path = "exercises/primitive_types/primitive_types2.rs"
|
path = "exercises/primitive_types/primitive_types2.rs"
|
||||||
mode = "compile"
|
mode = "compile"
|
||||||
|
hint = "No hints this time ;)"
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
name = "primitive_types3"
|
name = "primitive_types3"
|
||||||
path = "exercises/primitive_types/primitive_types3.rs"
|
path = "exercises/primitive_types/primitive_types3.rs"
|
||||||
mode = "compile"
|
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]]
|
[[exercises]]
|
||||||
name = "primitive_types4"
|
name = "primitive_types4"
|
||||||
path = "exercises/primitive_types/primitive_types4.rs"
|
path = "exercises/primitive_types/primitive_types4.rs"
|
||||||
mode = "test"
|
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]]
|
[[exercises]]
|
||||||
name = "primitive_types5"
|
name = "primitive_types5"
|
||||||
path = "exercises/primitive_types/primitive_types5.rs"
|
path = "exercises/primitive_types/primitive_types5.rs"
|
||||||
mode = "compile"
|
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]]
|
[[exercises]]
|
||||||
name = "primitive_types6"
|
name = "primitive_types6"
|
||||||
path = "exercises/primitive_types/primitive_types6.rs"
|
path = "exercises/primitive_types/primitive_types6.rs"
|
||||||
mode = "compile"
|
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
|
# STRUCTS
|
||||||
|
|
||||||
|
@ -99,11 +187,13 @@ mode = "compile"
|
||||||
name = "structs1"
|
name = "structs1"
|
||||||
path = "exercises/structs/structs1.rs"
|
path = "exercises/structs/structs1.rs"
|
||||||
mode = "test"
|
mode = "test"
|
||||||
|
hint = "No hints this time ;)"
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
name = "structs2"
|
name = "structs2"
|
||||||
path = "exercises/structs/structs2.rs"
|
path = "exercises/structs/structs2.rs"
|
||||||
mode = "test"
|
mode = "test"
|
||||||
|
hint = "No hints this time ;)"
|
||||||
|
|
||||||
# STRINGS
|
# STRINGS
|
||||||
|
|
||||||
|
@ -111,11 +201,22 @@ mode = "test"
|
||||||
name = "strings1"
|
name = "strings1"
|
||||||
path = "exercises/strings/strings1.rs"
|
path = "exercises/strings/strings1.rs"
|
||||||
mode = "compile"
|
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]]
|
[[exercises]]
|
||||||
name = "strings2"
|
name = "strings2"
|
||||||
path = "exercises/strings/strings2.rs"
|
path = "exercises/strings/strings2.rs"
|
||||||
mode = "compile"
|
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
|
# TEST 2
|
||||||
|
|
||||||
|
@ -123,6 +224,7 @@ mode = "compile"
|
||||||
name = "test2"
|
name = "test2"
|
||||||
path = "exercises/test2.rs"
|
path = "exercises/test2.rs"
|
||||||
mode = "compile"
|
mode = "compile"
|
||||||
|
hint = "No hints this time ;)"
|
||||||
|
|
||||||
# ENUMS
|
# ENUMS
|
||||||
|
|
||||||
|
@ -130,16 +232,22 @@ mode = "compile"
|
||||||
name = "enums1"
|
name = "enums1"
|
||||||
path = "exercises/enums/enums1.rs"
|
path = "exercises/enums/enums1.rs"
|
||||||
mode = "compile"
|
mode = "compile"
|
||||||
|
hint = """
|
||||||
|
Hint: The declaration of the enumeration type has not been defined yet."""
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
name = "enums2"
|
name = "enums2"
|
||||||
path = "exercises/enums/enums2.rs"
|
path = "exercises/enums/enums2.rs"
|
||||||
mode = "compile"
|
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]]
|
[[exercises]]
|
||||||
name = "enums3"
|
name = "enums3"
|
||||||
path = "exercises/enums/enums3.rs"
|
path = "exercises/enums/enums3.rs"
|
||||||
mode = "test"
|
mode = "test"
|
||||||
|
hint = "No hints this time ;)"
|
||||||
|
|
||||||
# TESTS
|
# TESTS
|
||||||
|
|
||||||
|
@ -147,16 +255,31 @@ mode = "test"
|
||||||
name = "tests1"
|
name = "tests1"
|
||||||
path = "exercises/tests/tests1.rs"
|
path = "exercises/tests/tests1.rs"
|
||||||
mode = "test"
|
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]]
|
[[exercises]]
|
||||||
name = "tests2"
|
name = "tests2"
|
||||||
path = "exercises/tests/tests2.rs"
|
path = "exercises/tests/tests2.rs"
|
||||||
mode = "test"
|
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]]
|
[[exercises]]
|
||||||
name = "tests3"
|
name = "tests3"
|
||||||
path = "exercises/tests/tests3.rs"
|
path = "exercises/tests/tests3.rs"
|
||||||
mode = "test"
|
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
|
# TEST 3
|
||||||
|
|
||||||
|
@ -164,6 +287,7 @@ mode = "test"
|
||||||
name = "test3"
|
name = "test3"
|
||||||
path = "exercises/test3.rs"
|
path = "exercises/test3.rs"
|
||||||
mode = "test"
|
mode = "test"
|
||||||
|
hint = "No hints this time ;)"
|
||||||
|
|
||||||
# MODULES
|
# MODULES
|
||||||
|
|
||||||
|
@ -171,11 +295,21 @@ mode = "test"
|
||||||
name = "modules1"
|
name = "modules1"
|
||||||
path = "exercises/modules/modules1.rs"
|
path = "exercises/modules/modules1.rs"
|
||||||
mode = "compile"
|
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]]
|
[[exercises]]
|
||||||
name = "modules2"
|
name = "modules2"
|
||||||
path = "exercises/modules/modules2.rs"
|
path = "exercises/modules/modules2.rs"
|
||||||
mode = "compile"
|
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
|
# MACROS
|
||||||
|
|
||||||
|
@ -183,28 +317,48 @@ mode = "compile"
|
||||||
name = "macros1"
|
name = "macros1"
|
||||||
path = "exercises/macros/macros1.rs"
|
path = "exercises/macros/macros1.rs"
|
||||||
mode = "compile"
|
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]]
|
[[exercises]]
|
||||||
name = "macros2"
|
name = "macros2"
|
||||||
path = "exercises/macros/macros2.rs"
|
path = "exercises/macros/macros2.rs"
|
||||||
mode = "compile"
|
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]]
|
[[exercises]]
|
||||||
name = "macros3"
|
name = "macros3"
|
||||||
path = "exercises/macros/macros3.rs"
|
path = "exercises/macros/macros3.rs"
|
||||||
mode = "compile"
|
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]]
|
[[exercises]]
|
||||||
name = "macros4"
|
name = "macros4"
|
||||||
path = "exercises/macros/macros4.rs"
|
path = "exercises/macros/macros4.rs"
|
||||||
mode = "compile"
|
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
|
# TEST 4
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
name = "test4"
|
name = "test4"
|
||||||
path = "exercises/test4.rs"
|
path = "exercises/test4.rs"
|
||||||
mode = "compile"
|
mode = "compile"
|
||||||
|
hint = "No hints this time ;)"
|
||||||
|
|
||||||
# MOVE SEMANTICS
|
# MOVE SEMANTICS
|
||||||
|
|
||||||
|
@ -212,21 +366,55 @@ mode = "compile"
|
||||||
name = "move_semantics1"
|
name = "move_semantics1"
|
||||||
path = "exercises/move_semantics/move_semantics1.rs"
|
path = "exercises/move_semantics/move_semantics1.rs"
|
||||||
mode = "compile"
|
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]]
|
[[exercises]]
|
||||||
name = "move_semantics2"
|
name = "move_semantics2"
|
||||||
path = "exercises/move_semantics/move_semantics2.rs"
|
path = "exercises/move_semantics/move_semantics2.rs"
|
||||||
mode = "compile"
|
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<i32>`
|
||||||
|
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]]
|
[[exercises]]
|
||||||
name = "move_semantics3"
|
name = "move_semantics3"
|
||||||
path = "exercises/move_semantics/move_semantics3.rs"
|
path = "exercises/move_semantics/move_semantics3.rs"
|
||||||
mode = "compile"
|
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]]
|
[[exercises]]
|
||||||
name = "move_semantics4"
|
name = "move_semantics4"
|
||||||
path = "exercises/move_semantics/move_semantics4.rs"
|
path = "exercises/move_semantics/move_semantics4.rs"
|
||||||
mode = "compile"
|
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
|
# ERROR HANDLING
|
||||||
|
|
||||||
|
@ -234,21 +422,78 @@ mode = "compile"
|
||||||
name = "errors1"
|
name = "errors1"
|
||||||
path = "exercises/error_handling/errors1.rs"
|
path = "exercises/error_handling/errors1.rs"
|
||||||
mode = "test"
|
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<String, String> 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]]
|
[[exercises]]
|
||||||
name = "errors2"
|
name = "errors2"
|
||||||
path = "exercises/error_handling/errors2.rs"
|
path = "exercises/error_handling/errors2.rs"
|
||||||
mode = "test"
|
mode = "test"
|
||||||
|
hint = """
|
||||||
|
One way to handle this is using a `match` statement on
|
||||||
|
`item_quantity.parse::<i32>()` 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]]
|
[[exercises]]
|
||||||
name = "errors3"
|
name = "errors3"
|
||||||
path = "exercises/error_handling/errors3.rs"
|
path = "exercises/error_handling/errors3.rs"
|
||||||
mode = "test"
|
mode = "test"
|
||||||
|
hint = """
|
||||||
|
If other functions can return a `Result`, why shouldn't `main`?"""
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
name = "errors4"
|
name = "errors4"
|
||||||
path = "exercises/error_handling/errorsn.rs"
|
path = "exercises/error_handling/errorsn.rs"
|
||||||
mode = "test"
|
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<dyn error::Error>,
|
||||||
|
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
|
# OPTIONS / RESULTS
|
||||||
|
|
||||||
|
@ -256,11 +501,21 @@ mode = "test"
|
||||||
name = "option1"
|
name = "option1"
|
||||||
path = "exercises/error_handling/option1.rs"
|
path = "exercises/error_handling/option1.rs"
|
||||||
mode = "test"
|
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]]
|
[[exercises]]
|
||||||
name = "option2"
|
name = "option2"
|
||||||
path = "exercises/error_handling/result1.rs"
|
path = "exercises/error_handling/result1.rs"
|
||||||
mode = "test"
|
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
|
# STANDARD LIBRARY TYPES
|
||||||
|
|
||||||
|
@ -268,21 +523,56 @@ mode = "test"
|
||||||
name = "arc1"
|
name = "arc1"
|
||||||
path = "exercises/standard_library_types/arc1.rs"
|
path = "exercises/standard_library_types/arc1.rs"
|
||||||
mode = "compile"
|
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]]
|
[[exercises]]
|
||||||
name = "iterators2"
|
name = "iterators2"
|
||||||
path = "exercises/standard_library_types/iterators2.rs"
|
path = "exercises/standard_library_types/iterators2.rs"
|
||||||
mode = "test"
|
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]]
|
[[exercises]]
|
||||||
name = "iterators3"
|
name = "iterators3"
|
||||||
path = "exercises/standard_library_types/iterators3.rs"
|
path = "exercises/standard_library_types/iterators3.rs"
|
||||||
mode = "test"
|
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]]
|
[[exercises]]
|
||||||
name = "iterators4"
|
name = "iterators4"
|
||||||
path = "exercises/standard_library_types/iterators4.rs"
|
path = "exercises/standard_library_types/iterators4.rs"
|
||||||
mode = "test"
|
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
|
# THREADS
|
||||||
|
|
||||||
|
@ -290,3 +580,35 @@ mode = "test"
|
||||||
name = "threads1"
|
name = "threads1"
|
||||||
path = "exercises/threads/threads1.rs"
|
path = "exercises/threads/threads1.rs"
|
||||||
mode = "compile"
|
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<T> 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 :)"""
|
||||||
|
|
|
@ -27,6 +27,7 @@ pub struct Exercise {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub path: PathBuf,
|
pub path: PathBuf,
|
||||||
pub mode: Mode,
|
pub mode: Mode,
|
||||||
|
pub hint: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Exercise {
|
impl Exercise {
|
||||||
|
@ -74,6 +75,7 @@ mod test {
|
||||||
name: String::from("example"),
|
name: String::from("example"),
|
||||||
path: PathBuf::from("example.rs"),
|
path: PathBuf::from("example.rs"),
|
||||||
mode: Mode::Test,
|
mode: Mode::Test,
|
||||||
|
hint: String::from(""),
|
||||||
};
|
};
|
||||||
exercise.clean();
|
exercise.clean();
|
||||||
assert!(!Path::new(&temp_file()).exists());
|
assert!(!Path::new(&temp_file()).exists());
|
||||||
|
|
20
src/main.rs
20
src/main.rs
|
@ -27,6 +27,12 @@ fn main() {
|
||||||
.about("Runs/Tests a single exercise")
|
.about("Runs/Tests a single exercise")
|
||||||
.arg(Arg::with_name("name").required(true).index(1)),
|
.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();
|
.get_matches();
|
||||||
|
|
||||||
if None == matches.subcommand_name() {
|
if None == matches.subcommand_name() {
|
||||||
|
@ -71,6 +77,20 @@ fn main() {
|
||||||
run(&exercise).unwrap_or_else(|_| std::process::exit(1));
|
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() {
|
if matches.subcommand_matches("verify").is_some() {
|
||||||
verify(&exercises).unwrap_or_else(|_| std::process::exit(1));
|
verify(&exercises).unwrap_or_else(|_| std::process::exit(1));
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,10 @@
|
||||||
name = "compFailure"
|
name = "compFailure"
|
||||||
path = "compFailure.rs"
|
path = "compFailure.rs"
|
||||||
mode = "compile"
|
mode = "compile"
|
||||||
|
hint = """"""
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
name = "testFailure"
|
name = "testFailure"
|
||||||
path = "testFailure.rs"
|
path = "testFailure.rs"
|
||||||
mode = "test"
|
mode = "test"
|
||||||
|
hint = """"""
|
||||||
|
|
|
@ -2,8 +2,10 @@
|
||||||
name = "compSuccess"
|
name = "compSuccess"
|
||||||
path = "compSuccess.rs"
|
path = "compSuccess.rs"
|
||||||
mode = "compile"
|
mode = "compile"
|
||||||
|
hint = """"""
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
name = "testSuccess"
|
name = "testSuccess"
|
||||||
path = "testSuccess.rs"
|
path = "testSuccess.rs"
|
||||||
mode = "test"
|
mode = "test"
|
||||||
|
hint = """"""
|
||||||
|
|
Loading…
Reference in a new issue