mirror of
https://github.com/rust-lang/rustlings.git
synced 2024-12-14 11:22:55 +02:00
1164 lines
40 KiB
TOML
1164 lines
40 KiB
TOML
# INTRO
|
|
|
|
[[exercises]]
|
|
name = "intro1"
|
|
path = "exercises/intro/intro1.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
Remove the I AM NOT DONE comment in the exercises/intro/intro1.rs file
|
|
to move on to the next exercise."""
|
|
|
|
[[exercises]]
|
|
name = "intro2"
|
|
path = "exercises/intro/intro2.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
Add an argument after the format string."""
|
|
|
|
# VARIABLES
|
|
|
|
[[exercises]]
|
|
name = "variables1"
|
|
path = "exercises/variables/variables1.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
The declaration on line 8 is missing a keyword that is needed in Rust
|
|
to create a new variable binding."""
|
|
|
|
[[exercises]]
|
|
name = "variables2"
|
|
path = "exercises/variables/variables2.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
The compiler message is saying that Rust cannot infer the type that the
|
|
variable binding `x` has with what is given here.
|
|
What happens if you annotate line 7 with a type annotation?
|
|
What if you give x a value?
|
|
What if you do both?
|
|
What type should x be, anyway?
|
|
What if x is the same type as 10? What if it's a different type?"""
|
|
|
|
[[exercises]]
|
|
name = "variables3"
|
|
path = "exercises/variables/variables3.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
Oops! In this exercise, we have a variable binding that we've created on
|
|
line 7, and we're trying to use it on line 8, but we haven't given it a
|
|
value. We can't print out something that isn't there; try giving x a value!
|
|
This is an error that can cause bugs that's very easy to make in any
|
|
programming language -- thankfully the Rust compiler has caught this for us!"""
|
|
|
|
[[exercises]]
|
|
name = "variables4"
|
|
path = "exercises/variables/variables4.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
In Rust, variable bindings are immutable by default. But here we're trying
|
|
to reassign a different value to x! There's a keyword we can use to make
|
|
a variable binding mutable instead."""
|
|
|
|
[[exercises]]
|
|
name = "variables5"
|
|
path = "exercises/variables/variables5.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
In variables4 we already learned how to make an immutable variable mutable
|
|
using a special keyword. Unfortunately this doesn't help us much in this exercise
|
|
because we want to assign a different typed value to an existing variable. Sometimes
|
|
you may also like to reuse existing variable names because you are just converting
|
|
values to different types like in this exercise.
|
|
Fortunately Rust has a powerful solution to this problem: 'Shadowing'!
|
|
You can read more about 'Shadowing' in the book's section 'Variables and Mutability':
|
|
https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#shadowing
|
|
Try to solve this exercise afterwards using this technique."""
|
|
|
|
[[exercises]]
|
|
name = "variables6"
|
|
path = "exercises/variables/variables6.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
We know about variables and mutability, but there is another important type of
|
|
variable available: constants.
|
|
Constants are always immutable and they are declared with keyword 'const' rather
|
|
than keyword 'let'.
|
|
Constants types must also always be annotated.
|
|
|
|
Read more about constants and the differences between variables and constants under 'Constants' in the book's section 'Variables and Mutability':
|
|
https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#constants
|
|
"""
|
|
|
|
# FUNCTIONS
|
|
|
|
[[exercises]]
|
|
name = "functions1"
|
|
path = "exercises/functions/functions1.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
This main function is calling a function that it expects to exist, but the
|
|
function doesn't exist. It expects this function to have the name `call_me`.
|
|
It expects this function to not take any arguments and not return a value.
|
|
Sounds a lot like `main`, doesn't it?"""
|
|
|
|
[[exercises]]
|
|
name = "functions2"
|
|
path = "exercises/functions/functions2.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
Rust requires that all parts of a function's signature have type annotations,
|
|
but `call_me` is missing the type annotation of `num`."""
|
|
|
|
[[exercises]]
|
|
name = "functions3"
|
|
path = "exercises/functions/functions3.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
This time, the function *declaration* is okay, but there's something wrong
|
|
with the place where we're calling the function.
|
|
As a reminder, you can freely play around with different solutions in Rustlings!
|
|
Watch mode will only jump to the next exercise if you remove the I AM NOT DONE comment."""
|
|
|
|
[[exercises]]
|
|
name = "functions4"
|
|
path = "exercises/functions/functions4.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
The error message points to line 17 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!
|
|
|
|
Also: Did you figure out that, technically, u32 would be the more fitting type
|
|
for the prices here, since they can't be negative? If so, kudos!"""
|
|
|
|
[[exercises]]
|
|
name = "functions5"
|
|
path = "exercises/functions/functions5.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
This is a really common error that can be fixed by removing one character.
|
|
It happens because Rust distinguishes between expressions and statements: expressions return a value based on their operand(s), 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`"""
|
|
|
|
# IF
|
|
|
|
[[exercises]]
|
|
name = "if1"
|
|
path = "exercises/if/if1.rs"
|
|
mode = "test"
|
|
hint = """
|
|
It's possible to do this in one line if you would like!
|
|
Some similar examples from other languages:
|
|
- In C(++) this would be: `a > b ? a : b`
|
|
- In Python this would be: `a if a > b else b`
|
|
Remember in Rust that:
|
|
- the `if` condition does not need to be surrounded by parentheses
|
|
- `if`/`else` conditionals are expressions
|
|
- Each condition is followed by a `{}` block."""
|
|
|
|
[[exercises]]
|
|
name = "if2"
|
|
path = "exercises/if/if2.rs"
|
|
mode = "test"
|
|
hint = """
|
|
For that first compiler error, it's important in Rust that each conditional
|
|
block returns the same type! To get the tests passing, you will need a couple
|
|
conditions checking different input values."""
|
|
|
|
# QUIZ 1
|
|
|
|
[[exercises]]
|
|
name = "quiz1"
|
|
path = "exercises/quiz1.rs"
|
|
mode = "test"
|
|
hint = "No hints this time ;)"
|
|
|
|
# PRIMITIVE TYPES
|
|
|
|
[[exercises]]
|
|
name = "primitive_types1"
|
|
path = "exercises/primitive_types/primitive_types1.rs"
|
|
mode = "compile"
|
|
hint = "No hints this time ;)"
|
|
|
|
[[exercises]]
|
|
name = "primitive_types2"
|
|
path = "exercises/primitive_types/primitive_types2.rs"
|
|
mode = "compile"
|
|
hint = "No hints this time ;)"
|
|
|
|
[[exercises]]
|
|
name = "primitive_types3"
|
|
path = "exercises/primitive_types/primitive_types3.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
There's a shorthand to initialize Arrays with a certain size that does not
|
|
require you to type in 100 items (but you certainly can if you want!).
|
|
For example, you can do:
|
|
let array = ["Are we there yet?"; 10];
|
|
|
|
Bonus: what are some other things you could have that would return true
|
|
for `a.len() >= 100`?"""
|
|
|
|
[[exercises]]
|
|
name = "primitive_types4"
|
|
path = "exercises/primitive_types/primitive_types4.rs"
|
|
mode = "test"
|
|
hint = """
|
|
Take a look at the Understanding Ownership -> Slices -> Other Slices section of the book:
|
|
https://doc.rust-lang.org/book/ch04-03-slices.html
|
|
and use the starting and ending indices of the items in the Array
|
|
that you want to end up in the slice.
|
|
|
|
If you're curious why the first argument of `assert_eq!` does not
|
|
have an ampersand for a reference since the second argument is a
|
|
reference, take a look at the coercion chapter of the nomicon:
|
|
https://doc.rust-lang.org/nomicon/coercions.html"""
|
|
|
|
[[exercises]]
|
|
name = "primitive_types5"
|
|
path = "exercises/primitive_types/primitive_types5.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
Take a look at the Data Types -> The Tuple Type section of the book:
|
|
https://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type
|
|
Particularly the part about destructuring (second to last example in the section).
|
|
You'll need to make a pattern to bind `name` and `age` to the appropriate parts
|
|
of the tuple. You can do it!!"""
|
|
|
|
[[exercises]]
|
|
name = "primitive_types6"
|
|
path = "exercises/primitive_types/primitive_types6.rs"
|
|
mode = "test"
|
|
hint = """
|
|
While you could use a destructuring `let` for the tuple here, try
|
|
indexing into it instead, as explained in the last example of the
|
|
Data Types -> The Tuple Type section of the book:
|
|
https://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type
|
|
Now you have another tool in your toolbox!"""
|
|
|
|
# VECS
|
|
|
|
[[exercises]]
|
|
name = "vecs1"
|
|
path = "exercises/vecs/vecs1.rs"
|
|
mode = "test"
|
|
hint = """
|
|
In Rust, there are two ways to define a Vector.
|
|
1. One way is to use the `Vec::new()` function to create a new vector
|
|
and fill it with the `push()` method.
|
|
2. The second way, which is simpler is to use the `vec![]` macro and
|
|
define your elements inside the square brackets.
|
|
Check this chapter: https://doc.rust-lang.org/stable/book/ch08-01-vectors.html
|
|
of the Rust book to learn more.
|
|
"""
|
|
|
|
[[exercises]]
|
|
name = "vecs2"
|
|
path = "exercises/vecs/vecs2.rs"
|
|
mode = "test"
|
|
hint = """
|
|
Hint 1: `i` is each element from the Vec as they are being iterated. Can you try
|
|
multiplying this?
|
|
|
|
Hint 2: For the first function, there's a way to directly access the numbers stored
|
|
in the Vec, using the * dereference operator. You can both access and write to the
|
|
number that way.
|
|
|
|
After you've completed both functions, decide for yourself which approach you like
|
|
better. What do you think is the more commonly used pattern under Rust developers?
|
|
"""
|
|
|
|
# MOVE SEMANTICS
|
|
|
|
[[exercises]]
|
|
name = "move_semantics1"
|
|
path = "exercises/move_semantics/move_semantics1.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
So you've got the "cannot borrow immutable local variable `vec1` as mutable" error on line 13,
|
|
right? The fix for this is going to be adding one keyword, and the addition is NOT on line 13
|
|
where the error is.
|
|
|
|
Also: Try accessing `vec0` after having called `fill_vec()`. See what happens!"""
|
|
|
|
[[exercises]]
|
|
name = "move_semantics2"
|
|
path = "exercises/move_semantics/move_semantics2.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
So, `vec0` is passed into the `fill_vec` function as an argument. In Rust,
|
|
when an argument is passed to a function and it's not explicitly returned,
|
|
you can't use the original variable anymore. We call this "moving" a variable.
|
|
Variables that are moved into a function (or block scope) and aren't explicitly
|
|
returned get "dropped" at the end of that function. This is also what happens here.
|
|
There's a few ways to fix this, try them all if you want:
|
|
1. Make another, separate version of the data that's in `vec0` and pass that
|
|
to `fill_vec` instead.
|
|
2. Make `fill_vec` borrow its argument instead of taking ownership of it,
|
|
and then copy the data within the function in order to return an owned
|
|
`Vec<i32>`
|
|
3. Make `fill_vec` *mutably* borrow a reference to its argument (which will need to be
|
|
mutable), modify it directly, then not return anything. Then you can get rid
|
|
of `vec1` entirely -- note that this will change what gets printed by the
|
|
first `println!`"""
|
|
|
|
[[exercises]]
|
|
name = "move_semantics3"
|
|
path = "exercises/move_semantics/move_semantics3.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
The difference between this one and the previous ones is that the first line
|
|
of `fn fill_vec` that had `let mut vec = vec;` is no longer there. You can,
|
|
instead of adding that line back, add `mut` in one place that will change
|
|
an existing binding to be a mutable binding instead of an immutable one :)"""
|
|
|
|
[[exercises]]
|
|
name = "move_semantics4"
|
|
path = "exercises/move_semantics/move_semantics4.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
Stop reading whenever you feel like you have enough direction :) Or try
|
|
doing one step and then fixing the compiler errors that result!
|
|
So the end goal is to:
|
|
- get rid of the first line in main that creates the new vector
|
|
- so then `vec0` doesn't exist, so we can't pass it to `fill_vec`
|
|
- we don't want to pass anything to `fill_vec`, so its signature should
|
|
reflect that it does not take any arguments
|
|
- since we're not creating a new vec in `main` anymore, we need to create
|
|
a new vec in `fill_vec`, similarly to the way we did in `main`"""
|
|
|
|
[[exercises]]
|
|
name = "move_semantics5"
|
|
path = "exercises/move_semantics/move_semantics5.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
Carefully reason about the range in which each mutable reference is in
|
|
scope. Does it help to update the value of referent (x) immediately after
|
|
the mutable reference is taken? Read more about 'Mutable References'
|
|
in the book's section References and Borrowing':
|
|
https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-references.
|
|
"""
|
|
|
|
[[exercises]]
|
|
name = "move_semantics6"
|
|
path = "exercises/move_semantics/move_semantics6.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
To find the answer, you can consult the book section "References and Borrowing":
|
|
https://doc.rust-lang.org/stable/book/ch04-02-references-and-borrowing.html
|
|
The first problem is that `get_char` is taking ownership of the string.
|
|
So `data` is moved and can't be used for `string_uppercase`
|
|
`data` is moved to `get_char` first, meaning that `string_uppercase` cannot manipulate the data.
|
|
Once you've fixed that, `string_uppercase`'s function signature will also need to be adjusted.
|
|
Can you figure out how?
|
|
|
|
Another hint: it has to do with the `&` character."""
|
|
|
|
# STRUCTS
|
|
|
|
[[exercises]]
|
|
name = "structs1"
|
|
path = "exercises/structs/structs1.rs"
|
|
mode = "test"
|
|
hint = """
|
|
Rust has more than one type of struct. Three actually, all variants are used to package related data together.
|
|
There are normal (or classic) structs. These are named collections of related data stored in fields.
|
|
Tuple structs are basically just named tuples.
|
|
Finally, Unit-like structs. These don't have any fields and are useful for generics.
|
|
|
|
In this exercise you need to complete and implement one of each kind.
|
|
Read more about structs in The Book: https://doc.rust-lang.org/book/ch05-01-defining-structs.html"""
|
|
|
|
[[exercises]]
|
|
name = "structs2"
|
|
path = "exercises/structs/structs2.rs"
|
|
mode = "test"
|
|
hint = """
|
|
Creating instances of structs is easy, all you need to do is assign some values to its fields.
|
|
There are however some shortcuts that can be taken when instantiating structs.
|
|
Have a look in The Book, to find out more: https://doc.rust-lang.org/stable/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax"""
|
|
|
|
[[exercises]]
|
|
name = "structs3"
|
|
path = "exercises/structs/structs3.rs"
|
|
mode = "test"
|
|
hint = """
|
|
For is_international: What makes a package international? Seems related to the places it goes through right?
|
|
|
|
For get_fees: This method takes an additional argument, is there a field in the Package struct that this relates to?
|
|
|
|
Have a look in The Book, to find out more about method implementations: https://doc.rust-lang.org/book/ch05-03-method-syntax.html"""
|
|
|
|
# ENUMS
|
|
|
|
[[exercises]]
|
|
name = "enums1"
|
|
path = "exercises/enums/enums1.rs"
|
|
mode = "compile"
|
|
hint = "No hints this time ;)"
|
|
|
|
[[exercises]]
|
|
name = "enums2"
|
|
path = "exercises/enums/enums2.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
You can create enumerations that have different variants with different types
|
|
such as no data, anonymous structs, a single string, tuples, ...etc"""
|
|
|
|
[[exercises]]
|
|
name = "enums3"
|
|
path = "exercises/enums/enums3.rs"
|
|
mode = "test"
|
|
hint = """
|
|
As a first step, you can define enums to compile this code without errors.
|
|
and then create a match expression in `process()`.
|
|
Note that you need to deconstruct some message variants
|
|
in the match expression to get value in the variant."""
|
|
|
|
# STRINGS
|
|
|
|
[[exercises]]
|
|
name = "strings1"
|
|
path = "exercises/strings/strings1.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
The `current_favorite_color` function is currently returning a string slice with the `'static`
|
|
lifetime. We know this because the data of the string lives in our code itself -- it doesn't
|
|
come from a file or user input or another program -- so it will live as long as our program
|
|
lives. But it is still a string slice. There's one way to create a `String` by converting a
|
|
string slice covered in the Strings chapter of the book, and another way that uses the `From`
|
|
trait."""
|
|
|
|
[[exercises]]
|
|
name = "strings2"
|
|
path = "exercises/strings/strings2.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
Yes, it would be really easy to fix this by just changing the value bound to `word` to be a
|
|
string slice instead of a `String`, wouldn't it?? There is a way to add one character to line
|
|
9, though, that will coerce the `String` into a string slice."""
|
|
|
|
[[exercises]]
|
|
name = "strings3"
|
|
path = "exercises/strings/strings3.rs"
|
|
mode = "test"
|
|
hint = """
|
|
There's tons of useful standard library functions for strings. Let's try and use some of
|
|
them: <https://doc.rust-lang.org/std/string/struct.String.html#method.trim>!
|
|
|
|
For the compose_me method: You can either use the `format!` macro, or convert the string
|
|
slice into an owned string, which you can then freely extend."""
|
|
|
|
[[exercises]]
|
|
name = "strings4"
|
|
path = "exercises/strings/strings4.rs"
|
|
mode = "compile"
|
|
hint = "No hints this time ;)"
|
|
|
|
# MODULES
|
|
|
|
[[exercises]]
|
|
name = "modules1"
|
|
path = "exercises/modules/modules1.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
Everything is private in Rust by default-- but there's a keyword we can use
|
|
to make something public! The compiler error should point to the thing that
|
|
needs to be public."""
|
|
|
|
[[exercises]]
|
|
name = "modules2"
|
|
path = "exercises/modules/modules2.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
The delicious_snacks module is trying to present an external interface that is
|
|
different than its internal structure (the `fruits` and `veggies` modules and
|
|
associated constants). Complete the `use` statements to fit the uses in main and
|
|
find the one keyword missing for both constants."""
|
|
|
|
[[exercises]]
|
|
name = "modules3"
|
|
path = "exercises/modules/modules3.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
UNIX_EPOCH and SystemTime are declared in the std::time module. Add a use statement
|
|
for these two to bring them into scope. You can use nested paths or the glob
|
|
operator to bring these two in using only one line."""
|
|
|
|
# HASHMAPS
|
|
|
|
[[exercises]]
|
|
name = "hashmaps1"
|
|
path = "exercises/hashmaps/hashmaps1.rs"
|
|
mode = "test"
|
|
hint = """
|
|
Hint 1: Take a look at the return type of the function to figure out
|
|
the type for the `basket`.
|
|
Hint 2: Number of fruits should be at least 5. And you have to put
|
|
at least three different types of fruits.
|
|
"""
|
|
|
|
[[exercises]]
|
|
name = "hashmaps2"
|
|
path = "exercises/hashmaps/hashmaps2.rs"
|
|
mode = "test"
|
|
hint = """
|
|
Use the `entry()` and `or_insert()` methods of `HashMap` to achieve this.
|
|
Learn more at https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value
|
|
"""
|
|
|
|
[[exercises]]
|
|
name = "hashmaps3"
|
|
path = "exercises/hashmaps/hashmaps3.rs"
|
|
mode = "test"
|
|
hint = """
|
|
Hint 1: Use the `entry()` and `or_insert()` methods of `HashMap` to insert entries corresponding to each team in the scores table.
|
|
Learn more at https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value
|
|
Hint 2: If there is already an entry for a given key, the value returned by `entry()` can be updated based on the existing value.
|
|
Learn more at https://doc.rust-lang.org/book/ch08-03-hash-maps.html#updating-a-value-based-on-the-old-value
|
|
"""
|
|
|
|
# QUIZ 2
|
|
|
|
[[exercises]]
|
|
name = "quiz2"
|
|
path = "exercises/quiz2.rs"
|
|
mode = "test"
|
|
hint = "No hints this time ;)"
|
|
|
|
# OPTIONS
|
|
|
|
[[exercises]]
|
|
name = "options1"
|
|
path = "exercises/options/options1.rs"
|
|
mode = "test"
|
|
hint = """
|
|
Options can have a Some value, with an inner value, or a None value, without an inner value.
|
|
There's multiple ways to get at the inner value, you can use unwrap, or pattern match. Unwrapping
|
|
is the easiest, but how do you do it safely so that it doesn't panic in your face later?"""
|
|
|
|
[[exercises]]
|
|
name = "options2"
|
|
path = "exercises/options/options2.rs"
|
|
mode = "test"
|
|
hint = """
|
|
check out:
|
|
https://doc.rust-lang.org/rust-by-example/flow_control/if_let.html
|
|
https://doc.rust-lang.org/rust-by-example/flow_control/while_let.html
|
|
|
|
Remember that Options can be stacked in if let and while let.
|
|
For example: Some(Some(variable)) = variable2
|
|
Also see Option::flatten
|
|
"""
|
|
|
|
[[exercises]]
|
|
name = "options3"
|
|
path = "exercises/options/options3.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
The compiler says a partial move happened in the `match`
|
|
statement. How can this be avoided? The compiler shows the correction
|
|
needed. After making the correction as suggested by the compiler, do
|
|
read: https://doc.rust-lang.org/std/keyword.ref.html"""
|
|
|
|
# ERROR HANDLING
|
|
|
|
[[exercises]]
|
|
name = "errors1"
|
|
path = "exercises/error_handling/errors1.rs"
|
|
mode = "test"
|
|
hint = """
|
|
`Ok` and `Err` are one of the variants of `Result`, so what the tests are 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`"""
|
|
|
|
[[exercises]]
|
|
name = "errors2"
|
|
path = "exercises/error_handling/errors2.rs"
|
|
mode = "test"
|
|
hint = """
|
|
One way to handle this is using a `match` statement on
|
|
`item_quantity.parse::<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]]
|
|
name = "errors3"
|
|
path = "exercises/error_handling/errors3.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
If other functions can return a `Result`, why shouldn't `main`? It's a fairly common
|
|
convention to return something like Result<(), ErrorType> from your main function.
|
|
The unit (`()`) type is there because nothing is really needed in terms of positive
|
|
results."""
|
|
|
|
[[exercises]]
|
|
name = "errors4"
|
|
path = "exercises/error_handling/errors4.rs"
|
|
mode = "test"
|
|
hint = """
|
|
`PositiveNonzeroInteger::new` is always creating a new instance and returning an `Ok` result.
|
|
It should be doing some checking, returning an `Err` result if those checks fail, and only
|
|
returning an `Ok` result if those checks determine that everything is... okay :)"""
|
|
|
|
[[exercises]]
|
|
name = "errors5"
|
|
path = "exercises/error_handling/errors5.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
There are two different possible `Result` types produced within `main()`, which are
|
|
propagated using `?` operators. How do we declare a return type from `main()` that allows both?
|
|
|
|
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>`. This boxed trait object is polymorphic, and since all
|
|
errors implement the `error::Error` trait, we can capture lots of different errors in one "Box"
|
|
object.
|
|
|
|
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
|
|
|
|
Read more about boxing errors:
|
|
https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/boxing_errors.html
|
|
|
|
Read more about using the `?` operator with boxed errors:
|
|
https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html
|
|
"""
|
|
|
|
[[exercises]]
|
|
name = "errors6"
|
|
path = "exercises/error_handling/errors6.rs"
|
|
mode = "test"
|
|
hint = """
|
|
This exercise uses a completed version of `PositiveNonzeroInteger` from
|
|
errors4.
|
|
|
|
Below the line that TODO asks you to change, there is an example of using
|
|
the `map_err()` method on a `Result` to transform one type of error into
|
|
another. Try using something similar on the `Result` from `parse()`. You
|
|
might use the `?` operator to return early from the function, or you might
|
|
use a `match` expression, or maybe there's another way!
|
|
|
|
You can create another function inside `impl ParsePosNonzeroError` to use
|
|
with `map_err()`.
|
|
|
|
Read more about `map_err()` in the `std::result` documentation:
|
|
https://doc.rust-lang.org/std/result/enum.Result.html#method.map_err"""
|
|
|
|
# Generics
|
|
|
|
[[exercises]]
|
|
name = "generics1"
|
|
path = "exercises/generics/generics1.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
Vectors in Rust make use of generics to create dynamically sized arrays of any type.
|
|
You need to tell the compiler what type we are pushing onto this vector."""
|
|
|
|
[[exercises]]
|
|
name = "generics2"
|
|
path = "exercises/generics/generics2.rs"
|
|
mode = "test"
|
|
hint = """
|
|
Currently we are wrapping only values of type 'u32'.
|
|
Maybe we could update the explicit references to this data type somehow?
|
|
|
|
If you are still stuck https://doc.rust-lang.org/stable/book/ch10-01-syntax.html#in-method-definitions
|
|
"""
|
|
|
|
# TRAITS
|
|
|
|
[[exercises]]
|
|
name = "traits1"
|
|
path = "exercises/traits/traits1.rs"
|
|
mode = "test"
|
|
hint = """
|
|
A discussion about Traits in Rust can be found at:
|
|
https://doc.rust-lang.org/book/ch10-02-traits.html
|
|
"""
|
|
|
|
[[exercises]]
|
|
name = "traits2"
|
|
path = "exercises/traits/traits2.rs"
|
|
mode = "test"
|
|
hint = """
|
|
Notice how the trait takes ownership of 'self',and returns `Self`.
|
|
Try mutating the incoming string vector. Have a look at the tests to see
|
|
what the result should look like!
|
|
|
|
Vectors provide suitable methods for adding an element at the end. See
|
|
the documentation at: https://doc.rust-lang.org/std/vec/struct.Vec.html"""
|
|
|
|
[[exercises]]
|
|
name = "traits3"
|
|
path = "exercises/traits/traits3.rs"
|
|
mode = "test"
|
|
hint = """
|
|
Traits can have a default implementation for functions. Structs that implement
|
|
the trait can then use the default version of these functions if they choose not
|
|
implement the function themselves.
|
|
|
|
See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#default-implementations
|
|
"""
|
|
|
|
[[exercises]]
|
|
name = "traits4"
|
|
path = "exercises/traits/traits4.rs"
|
|
mode = "test"
|
|
hint = """
|
|
Instead of using concrete types as parameters you can use traits. Try replacing the
|
|
'??' with 'impl <what goes here?>'
|
|
|
|
See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
|
|
"""
|
|
|
|
[[exercises]]
|
|
name = "traits5"
|
|
path = "exercises/traits/traits5.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
To ensure a parameter implements multiple traits use the '+ syntax'. Try replacing the
|
|
'??' with 'impl <> + <>'.
|
|
|
|
See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#specifying-multiple-trait-bounds-with-the--syntax
|
|
"""
|
|
|
|
# QUIZ 3
|
|
|
|
[[exercises]]
|
|
name = "quiz3"
|
|
path = "exercises/quiz3.rs"
|
|
mode = "test"
|
|
hint = """
|
|
To find the best solution to this challenge you're going to need to think back to your
|
|
knowledge of traits, specifically Trait Bound Syntax - you may also need this: `use std::fmt::Display;`."""
|
|
|
|
# TESTS
|
|
|
|
[[exercises]]
|
|
name = "tests1"
|
|
path = "exercises/tests/tests1.rs"
|
|
mode = "test"
|
|
hint = """
|
|
You don't even need to write any code to test -- you can just test values and run that, even
|
|
though you wouldn't do that in real life :) `assert!` is a macro that needs an argument.
|
|
Depending on the value of the argument, `assert!` will do nothing (in which case the test will
|
|
pass) or `assert!` will panic (in which case the test will fail). So try giving different values
|
|
to `assert!` and see which ones compile, which ones pass, and which ones fail :)"""
|
|
|
|
[[exercises]]
|
|
name = "tests2"
|
|
path = "exercises/tests/tests2.rs"
|
|
mode = "test"
|
|
hint = """
|
|
Like the previous exercise, you don't need to write any code to get this test to compile and
|
|
run. `assert_eq!` is a macro that takes two arguments and compares them. Try giving it two
|
|
values that are equal! Try giving it two arguments that are different! Try giving it two values
|
|
that are of different types! Try switching which argument comes first and which comes second!"""
|
|
|
|
[[exercises]]
|
|
name = "tests3"
|
|
path = "exercises/tests/tests3.rs"
|
|
mode = "test"
|
|
hint = """
|
|
You can call a function right where you're passing arguments to `assert!` -- so you could do
|
|
something like `assert!(having_fun())`. If you want to check that you indeed get false, you
|
|
can negate the result of what you're doing using `!`, like `assert!(!having_fun())`."""
|
|
|
|
# LIFETIMES
|
|
|
|
[[exercises]]
|
|
name = "lifetimes1"
|
|
path = "exercises/lifetimes/lifetimes1.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
Let the compiler guide you. Also take a look at the book if you need help:
|
|
https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html"""
|
|
|
|
[[exercises]]
|
|
name = "lifetimes2"
|
|
path = "exercises/lifetimes/lifetimes2.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
Remember that the generic lifetime 'a will get the concrete lifetime that is equal to the smaller of the lifetimes of x and y.
|
|
You can take at least two paths to achieve the desired result while keeping the inner block:
|
|
1. Move the string2 declaration to make it live as long as string1 (how is result declared?)
|
|
2. Move println! into the inner block"""
|
|
|
|
[[exercises]]
|
|
name = "lifetimes3"
|
|
path = "exercises/lifetimes/lifetimes3.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
If you use a lifetime annotation in a struct's fields, where else does it need to be added?"""
|
|
|
|
# STANDARD LIBRARY TYPES
|
|
|
|
[[exercises]]
|
|
name = "iterators1"
|
|
path = "exercises/iterators/iterators1.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
Step 1:
|
|
We need to apply something to the collection `my_fav_fruits` before we start to go through
|
|
it. What could that be? Take a look at the struct definition for a vector for inspiration:
|
|
https://doc.rust-lang.org/std/vec/struct.Vec.html
|
|
Step 2 & step 3:
|
|
Very similar to the lines above and below. You've got this!
|
|
Step 4:
|
|
An iterator goes through all elements in a collection, but what if we've run out of
|
|
elements? What should we expect here? If you're stuck, take a look at
|
|
https://doc.rust-lang.org/std/iter/trait.Iterator.html for some ideas.
|
|
"""
|
|
|
|
[[exercises]]
|
|
name = "iterators2"
|
|
path = "exercises/iterators/iterators2.rs"
|
|
mode = "test"
|
|
hint = """
|
|
Step 1
|
|
The variable `first` is a `char`. It needs to be capitalized and added to the
|
|
remaining characters in `c` in order to return the correct `String`.
|
|
The remaining characters in `c` can be viewed as a string slice using the
|
|
`as_str` method.
|
|
The documentation for `char` contains many useful methods.
|
|
https://doc.rust-lang.org/std/primitive.char.html
|
|
|
|
Step 2
|
|
Create an iterator from the slice. Transform the iterated values by applying
|
|
the `capitalize_first` function. Remember to collect the iterator.
|
|
|
|
Step 3.
|
|
This is surprising similar to the previous solution. Collect is very powerful
|
|
and very general. Rust just needs to know the desired type."""
|
|
|
|
[[exercises]]
|
|
name = "iterators3"
|
|
path = "exercises/iterators/iterators3.rs"
|
|
mode = "test"
|
|
hint = """
|
|
The divide function needs to return the correct error when even division is not
|
|
possible.
|
|
|
|
The division_results variable needs to be collected into a collection type.
|
|
|
|
The result_with_list function needs to return a single Result where the success
|
|
case is a vector of integers and the failure case is a DivisionError.
|
|
|
|
The list_of_results function needs to return a vector of results.
|
|
|
|
See https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect for how
|
|
the `FromIterator` trait is used in `collect()`. This trait is REALLY powerful! It
|
|
can make the solution to this exercise infinitely easier."""
|
|
|
|
[[exercises]]
|
|
name = "iterators4"
|
|
path = "exercises/iterators/iterators4.rs"
|
|
mode = "test"
|
|
hint = """
|
|
In an imperative language, you might write a for loop that updates
|
|
a mutable variable. Or, you might write code utilizing recursion
|
|
and a match clause. In Rust you can take another functional
|
|
approach, computing the factorial elegantly with ranges and iterators.
|
|
|
|
Hint 2: Check out the `fold` and `rfold` methods!"""
|
|
|
|
[[exercises]]
|
|
name = "iterators5"
|
|
path = "exercises/iterators/iterators5.rs"
|
|
mode = "test"
|
|
hint = """
|
|
The documentation for the std::iter::Iterator trait contains numerous methods
|
|
that would be helpful here.
|
|
|
|
Return 0 from count_collection_iterator to make the code compile in order to
|
|
test count_iterator.
|
|
|
|
The collection variable in count_collection_iterator is a slice of HashMaps. It
|
|
needs to be converted into an iterator in order to use the iterator methods.
|
|
|
|
The fold method can be useful in the count_collection_iterator function.
|
|
|
|
For a further challenge, consult the documentation for Iterator to find
|
|
a different method that could make your code more compact than using fold."""
|
|
|
|
# THREADS
|
|
|
|
[[exercises]]
|
|
name = "threads1"
|
|
path = "exercises/threads/threads1.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
`JoinHandle` is a struct that is returned from a spawned thread:
|
|
https://doc.rust-lang.org/std/thread/fn.spawn.html
|
|
|
|
A challenge with multi-threaded applications is that the main thread can
|
|
finish before the spawned threads are completed.
|
|
https://doc.rust-lang.org/book/ch16-01-threads.html#waiting-for-all-threads-to-finish-using-join-handles
|
|
|
|
Collect the JoinHandles and wait for them to finish.
|
|
https://doc.rust-lang.org/std/thread/struct.JoinHandle.html
|
|
"""
|
|
|
|
[[exercises]]
|
|
name = "threads2"
|
|
path = "exercises/threads/threads2.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
`Arc` is an Atomic Reference Counted pointer that allows safe, shared access
|
|
to **immutable** data. But we want to *change* the number of `jobs_completed`
|
|
so we'll need to also use another type that will only allow one thread to
|
|
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.
|
|
|
|
If you've learned from the sample solutions, I encourage you to come
|
|
back to this exercise and try it again in a few days to reinforce
|
|
what you've learned :)"""
|
|
|
|
[[exercises]]
|
|
name = "threads3"
|
|
path = "exercises/threads/threads3.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
An alternate way to handle concurrency between threads is to use
|
|
a mpsc (multiple producer, single consumer) channel to communicate.
|
|
With both a sending end and a receiving end, it's possible to
|
|
send values in one thread and receive them in another.
|
|
Multiple producers are possible by using clone() to create a duplicate
|
|
of the original sending end.
|
|
See https://doc.rust-lang.org/book/ch16-02-message-passing.html for more info.
|
|
"""
|
|
|
|
# SMART POINTERS
|
|
|
|
[[exercises]]
|
|
name = "box1"
|
|
path = "exercises/smart_pointers/box1.rs"
|
|
mode = "test"
|
|
hint = """
|
|
Step 1
|
|
The compiler's message should help: since we cannot store the value of the actual type
|
|
when working with recursive types, we need to store a reference (pointer) to its value.
|
|
We should, therefore, place our `List` inside a `Box`. More details in the book here:
|
|
https://doc.rust-lang.org/book/ch15-01-box.html#enabling-recursive-types-with-boxes
|
|
|
|
Step 2
|
|
Creating an empty list should be fairly straightforward (hint: peek at the assertions).
|
|
For a non-empty list keep in mind that we want to use our Cons "list builder".
|
|
Although the current list is one of integers (i32), feel free to change the definition
|
|
and try other types!
|
|
"""
|
|
|
|
[[exercises]]
|
|
name = "rc1"
|
|
path = "exercises/smart_pointers/rc1.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
This is a straightforward exercise to use the Rc<T> type. Each Planet has
|
|
ownership of the Sun, and uses Rc::clone() to increment the reference count of the Sun.
|
|
After using drop() to move the Planets out of scope individually, the reference count goes down.
|
|
In the end the sun only has one reference again, to itself. See more at:
|
|
https://doc.rust-lang.org/book/ch15-04-rc.html
|
|
|
|
* Unfortunately Pluto is no longer considered a planet :(
|
|
"""
|
|
|
|
[[exercises]]
|
|
name = "arc1"
|
|
path = "exercises/smart_pointers/arc1.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
Make `shared_numbers` be an `Arc` from the numbers vector. Then, in order
|
|
to avoid creating a copy of `numbers`, you'll need to create `child_numbers`
|
|
inside the loop but still in the main thread.
|
|
|
|
`child_numbers` should be a clone of the Arc of the numbers instead of a
|
|
thread-local copy of the numbers.
|
|
|
|
This is a simple exercise if you understand the underlying concepts, but if this
|
|
is too much of a struggle, consider reading through all of Chapter 16 in the book:
|
|
https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html
|
|
"""
|
|
|
|
[[exercises]]
|
|
name = "cow1"
|
|
path = "exercises/smart_pointers/cow1.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
Since the vector is already owned, the `Cow` type doesn't need to clone it.
|
|
|
|
Checkout https://doc.rust-lang.org/std/borrow/enum.Cow.html for documentation
|
|
on the `Cow` type.
|
|
"""
|
|
|
|
# MACROS
|
|
|
|
[[exercises]]
|
|
name = "macros1"
|
|
path = "exercises/macros/macros1.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
When you call a macro, you need to add something special compared to a
|
|
regular function call. If you're stuck, take a look at what's inside
|
|
`my_macro`."""
|
|
|
|
[[exercises]]
|
|
name = "macros2"
|
|
path = "exercises/macros/macros2.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
Macros don't quite play by the same rules as the rest of Rust, in terms of
|
|
what's available where.
|
|
|
|
Unlike other things in Rust, the order of "where you define a macro" versus
|
|
"where you use it" actually matters."""
|
|
|
|
[[exercises]]
|
|
name = "macros3"
|
|
path = "exercises/macros/macros3.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
In order to use a macro outside of its module, you need to do something
|
|
special to the module to lift the macro out into its parent.
|
|
|
|
The same trick also works on "extern crate" statements for crates that have
|
|
exported macros, if you've seen any of those around."""
|
|
|
|
[[exercises]]
|
|
name = "macros4"
|
|
path = "exercises/macros/macros4.rs"
|
|
mode = "compile"
|
|
hint = """
|
|
You only need to add a single character to make this compile.
|
|
The way macros are written, it wants to see something between each
|
|
"macro arm", so it can separate them.
|
|
|
|
That's all the macro exercises we have in here, but it's barely even
|
|
scratching the surface of what you can do with Rust's macros. For a more
|
|
thorough introduction, you can have a read through the little book of Rust
|
|
macros: https://veykril.github.io/tlborm/"""
|
|
|
|
# CLIPPY
|
|
|
|
[[exercises]]
|
|
name = "clippy1"
|
|
path = "exercises/clippy/clippy1.rs"
|
|
mode = "clippy"
|
|
hint = """
|
|
Rust stores the highest precision version of any long or infinite precision
|
|
mathematical constants in the Rust standard library.
|
|
https://doc.rust-lang.org/stable/std/f32/consts/index.html
|
|
|
|
We may be tempted to use our own approximations for certain mathematical constants,
|
|
but clippy recognizes those imprecise mathematical constants as a source of
|
|
potential error.
|
|
See the suggestions of the clippy warning in compile output and use the
|
|
appropriate replacement constant from std::f32::consts..."""
|
|
|
|
[[exercises]]
|
|
name = "clippy2"
|
|
path = "exercises/clippy/clippy2.rs"
|
|
mode = "clippy"
|
|
hint = """
|
|
`for` loops over Option values are more clearly expressed as an `if let`"""
|
|
|
|
[[exercises]]
|
|
name = "clippy3"
|
|
path = "exercises/clippy/clippy3.rs"
|
|
mode = "clippy"
|
|
hint = "No hints this time!"
|
|
|
|
# TYPE CONVERSIONS
|
|
|
|
[[exercises]]
|
|
name = "using_as"
|
|
path = "exercises/conversions/using_as.rs"
|
|
mode = "test"
|
|
hint = """
|
|
Use the `as` operator to cast one of the operands in the last line of the
|
|
`average` function into the expected return type."""
|
|
|
|
[[exercises]]
|
|
name = "from_into"
|
|
path = "exercises/conversions/from_into.rs"
|
|
mode = "test"
|
|
hint = """
|
|
Follow the steps provided right before the `From` implementation"""
|
|
|
|
[[exercises]]
|
|
name = "from_str"
|
|
path = "exercises/conversions/from_str.rs"
|
|
mode = "test"
|
|
hint = """
|
|
The implementation of FromStr should return an Ok with a Person object,
|
|
or an Err with an error if the string is not valid.
|
|
|
|
This is almost like the `from_into` exercise, but returning errors instead
|
|
of falling back to a default value.
|
|
|
|
Look at the test cases to see which error variants to return.
|
|
|
|
Another hint: You can use the `map_err` method of `Result` with a function
|
|
or a closure to wrap the error from `parse::<usize>`.
|
|
|
|
Yet another hint: If you would like to propagate errors by using the `?`
|
|
operator in your solution, you might want to look at
|
|
https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html
|
|
"""
|
|
|
|
[[exercises]]
|
|
name = "try_from_into"
|
|
path = "exercises/conversions/try_from_into.rs"
|
|
mode = "test"
|
|
hint = """
|
|
Follow the steps provided right before the `TryFrom` implementation.
|
|
You can also use the example at https://doc.rust-lang.org/std/convert/trait.TryFrom.html
|
|
|
|
Is there an implementation of `TryFrom` in the standard library that
|
|
can both do the required integer conversion and check the range of the input?
|
|
|
|
Another hint: Look at the test cases to see which error variants to return.
|
|
|
|
Yet another hint: You can use the `map_err` or `or` methods of `Result` to
|
|
convert errors.
|
|
|
|
Yet another hint: If you would like to propagate errors by using the `?`
|
|
operator in your solution, you might want to look at
|
|
https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html
|
|
|
|
Challenge: Can you make the `TryFrom` implementations generic over many integer types?"""
|
|
|
|
[[exercises]]
|
|
name = "as_ref_mut"
|
|
path = "exercises/conversions/as_ref_mut.rs"
|
|
mode = "test"
|
|
hint = """
|
|
Add AsRef<str> as a trait bound to the functions."""
|