|
|
# VARIABLES
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "variables1"
|
|
|
path = "exercises/variables/variables1.rs"
|
|
|
mode = "compile"
|
|
|
hint = """
|
|
|
提示: 第 12 行的变量声明缺少了一个关键字,在 Rust 中,创建一个
|
|
|
新的变量绑定必须用到这个关键字。"""
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "variables2"
|
|
|
path = "exercises/variables/variables2.rs"
|
|
|
mode = "compile"
|
|
|
hint = """
|
|
|
编译器在说,Rust 无法根据给定内容推断出变量 `x` 的类型.
|
|
|
如果你对第 7 行标注类型,会发生什么?
|
|
|
如果你对 x 赋予一个值呢?
|
|
|
如果你同时做到了以上两点呢?
|
|
|
x 到底是什么类型?
|
|
|
如果 x 与 10 是同一类型,亦或者它是不同的类型呢?"""
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "variables3"
|
|
|
path = "exercises/variables/variables3.rs"
|
|
|
mode = "compile"
|
|
|
hint = """
|
|
|
在 Rust,变量绑定默认是不可变的。但我们正试图重新分配
|
|
|
一个不同的值给 x !我们可以使用一个关键字使变量可变。"""
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "variables4"
|
|
|
path = "exercises/variables/variables4.rs"
|
|
|
mode = "compile"
|
|
|
hint = """
|
|
|
糟了!在这个练习中,我们在第 7 行创建了一个变量,然后试图在第 8 行
|
|
|
使用它,但是它并没被赋值!我们无法打印出不存在的内容,所以尝试赋予 x 一个值!
|
|
|
这个错误造成的 Bug 在任何编程语言中都非常容易发生——感谢 Rust 编译器提醒了我们"""
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "variables5"
|
|
|
path = "exercises/variables/variables5.rs"
|
|
|
mode = "compile"
|
|
|
hint = """
|
|
|
在 variables3 中,我们已经学会了使用一个特殊的关键字使一个不可变的变量变得可变。
|
|
|
可惜的是,在这个练习中,这个方法并不管用,因为我们想给一个现有的变量分配一个不
|
|
|
同类型的值。有时,你会想重复使用现有的变量名称,因为你只是将数值转换为不同的类型,就像
|
|
|
本练习中一样。幸运的是,Rust 有一个强大的技术可以解决这个问题:变量遮蔽(Shadowing)!
|
|
|
有关变量遮蔽的更多内容可通过这本书的 'Variables and Mutability'* 章节了解:
|
|
|
https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#shadowing
|
|
|
尝试使用此技术解决此练习。
|
|
|
|
|
|
译:Variables and Mutability:变量与可变性"""
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "variables6"
|
|
|
path = "exercises/variables/variables6.rs"
|
|
|
mode = "compile"
|
|
|
hint = """
|
|
|
我们已经了解了变量与可变性,但还有另一种重要的变量类型;常量(Constant)。
|
|
|
常量永远不可改变的,它用关键字 'const' 而非关键字 'let' 声明,并且其类型也必须被标注。
|
|
|
|
|
|
更多关于常量的信息 'Differences Between Variables and Constants'* 在这本书的章节 'Variables and Mutability':
|
|
|
https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#differences-between-variables-and-constants
|
|
|
|
|
|
译:Differences Between Variables and Constants:变量与常量的区别
|
|
|
"""
|
|
|
|
|
|
# FUNCTIONS
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "functions1"
|
|
|
path = "exercises/functions/functions1.rs"
|
|
|
mode = "compile"
|
|
|
hint = """
|
|
|
主函数中正试图调用一个名为 `call_me` 的函数,可这个函数并不存在。
|
|
|
它希望这个函数不接受任何参数,同时也不返回值。
|
|
|
听起来很像 `main` 函数,不是吗?"""
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "functions2"
|
|
|
path = "exercises/functions/functions2.rs"
|
|
|
mode = "compile"
|
|
|
hint = """
|
|
|
Rust 要求函数签名(signature)有类型标注,但是 `call_me` 函数缺少 `num` 的类型标注。"""
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "functions3"
|
|
|
path = "exercises/functions/functions3.rs"
|
|
|
mode = "compile"
|
|
|
hint = """
|
|
|
此时, 函数 *声明(declaration)* 是没问题的,但函数调用出了问题"""
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "functions4"
|
|
|
path = "exercises/functions/functions4.rs"
|
|
|
mode = "compile"
|
|
|
hint = """
|
|
|
错误信息指向第 15 行,说希望在`->`之后有一个类型。
|
|
|
那个地方标注了函数的返回类型——看看 `is_even` 函数的示例吧"""
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "functions5"
|
|
|
path = "exercises/functions/functions5.rs"
|
|
|
mode = "compile"
|
|
|
hint = """
|
|
|
这是一个非常常见的错误,可以通过删除一个字符来解决。
|
|
|
发生的原因是 Rust 区分了表达式和语句:表达式根据其运算数(operand)返回一个值,
|
|
|
而语句仅返回一个 `()` 类型,其行为好比 C/C++ 中的 `void` 。
|
|
|
我们希望 `square` 函数返回一个 `i32` 类型的值,但现在它返回的是 `()` 类型...
|
|
|
它们显然是不一样的。对此有两种解决方案。
|
|
|
1. 在 `num * num;` 前面加上 `return` 关键字
|
|
|
2. 移除 `;`,让它变成 `num * num`"""
|
|
|
|
|
|
# IF
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "if1"
|
|
|
path = "exercises/if/if1.rs"
|
|
|
mode = "test"
|
|
|
hint = """
|
|
|
如果你愿意的话,也可以用一行来做这件事!
|
|
|
其他语言中的一些类似例子:
|
|
|
- 在 C(++) 中会是: `a > b ? a : b`
|
|
|
- 在 Python 中会是: `a if a > b else b`
|
|
|
请记住在 Rust 中:
|
|
|
- `if` 的条件不需要用圆括号括起来
|
|
|
- `if`/`else` 的条件是表达式
|
|
|
- 每个条件后面都有一个 `{}` 块。"""
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "if2"
|
|
|
path = "exercises/if/if2.rs"
|
|
|
mode = "test"
|
|
|
hint = """
|
|
|
对于第一个编译错误,在于 Rust 中的重要一点:
|
|
|
每个条件块(conditional block)都必须返回相同的类型。
|
|
|
为了通过测试,你需要几个条件用来判断不同的输入"""
|
|
|
|
|
|
# TEST 1
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "quiz1"
|
|
|
path = "exercises/quiz1.rs"
|
|
|
mode = "test"
|
|
|
hint = "No hints this time ;)"
|
|
|
|
|
|
# MOVE SEMANTICS
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "move_semantics1"
|
|
|
path = "exercises/move_semantics/move_semantics1.rs"
|
|
|
mode = "compile"
|
|
|
hint = """
|
|
|
在第 13 行有个 "cannot borrow immutable local variable `vec1` as mutable"* 错误,对吗?
|
|
|
修复错误的方法是添加一个关键词,并且添加的位置不在报错的第 13 行上。
|
|
|
|
|
|
译注:不能将不可变的局部变量 `vec1` 借用为可变变量"""
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "move_semantics2"
|
|
|
path = "exercises/move_semantics/move_semantics2.rs"
|
|
|
mode = "compile"
|
|
|
hint = """
|
|
|
当我们在第 10 行调用 `fill_vec` 时,`vec0' 被 *移动(moved)* 到
|
|
|
函数 `fill_vec` 中,这意味着它会在 `fill_vec` 函数的末尾被丢弃,同时也
|
|
|
导致了我们不能在第 13 行再次使用 `vec0`(或在 `main` 中调用 `fill_vec` 后的任何地方)。
|
|
|
我们可以用几种方法来解决这个问题,都试一试吧!
|
|
|
1. 做一个 `vec0` 数据的拷贝,并将其传递给 `fill_vec` 。
|
|
|
2. 让 `fill_vec` 通过借用而不是获取所有权的方式获取参数,然后在函数中复制一份数据,以便返回
|
|
|
一个具有所有权的 `Vec<i32>` 变量。
|
|
|
3. 让 `fill_vec` 借用可变参数(参数也需要可变),直接进行操作,然后不返回任何东西。接着你需要
|
|
|
完全地去掉 `vec1`——但注意,这也将改变第一个 `println!` 打印出内容。"""
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "move_semantics3"
|
|
|
path = "exercises/move_semantics/move_semantics3.rs"
|
|
|
mode = "compile"
|
|
|
hint = """
|
|
|
与之前不同:`fn fill_vec` 第一行的 `let mut vec = vec;` 现在已经不存在了。
|
|
|
你可以在某个地方添加 `mut` 以使现有的不可变绑定变得可变,而非把不同的那一行加回去 :)"""
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "move_semantics4"
|
|
|
path = "exercises/move_semantics/move_semantics4.rs"
|
|
|
mode = "compile"
|
|
|
hint = """
|
|
|
只要你觉得有确切的目标,就可以停止阅读 :) 或者试着做一个步骤,然后修复编译错误。
|
|
|
因此,目标有:
|
|
|
- 去掉 main 中创建新 vector 的第一行
|
|
|
- 所以 `vec0` 已不存在了,不能再把它传给 `fill_vec` 。
|
|
|
- 现已不需要向 `fill_vec` 传递任何东西,所以它的(函数)签名应该反映出它不接受任何参数*。
|
|
|
- 由于已不在 `main` 创建 vector ,所以需要在 `fill_vec` 中创建一个新的 vector,
|
|
|
类似于 `main` 中的做法。
|
|
|
|
|
|
译注:练习中 fill_vec 的函数签名已经没有接受参数了,所以估计是在调用的地方"""
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "move_semantics5"
|
|
|
path = "exercises/move_semantics/move_semantics5.rs"
|
|
|
mode = "compile"
|
|
|
hint = """
|
|
|
仔细推敲每个可变引用的使用范围。
|
|
|
在获取可变引用后是否能够立即更新引用(x)的值?
|
|
|
在本书的 'References and Borrowing' 部分了解更多关于 'Mutable References' 的信息。
|
|
|
https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-references.
|
|
|
"""
|
|
|
|
|
|
# PRIMITIVE TYPES
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "primitive_types1"
|
|
|
path = "exercises/primitive_types/primitive_types1.rs"
|
|
|
mode = "compile"
|
|
|
hint = "这次没有提示 ;)"
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "primitive_types2"
|
|
|
path = "exercises/primitive_types/primitive_types2.rs"
|
|
|
mode = "compile"
|
|
|
hint = "这次没有提示 ;)"
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "primitive_types3"
|
|
|
path = "exercises/primitive_types/primitive_types3.rs"
|
|
|
mode = "compile"
|
|
|
hint = """
|
|
|
有一种简便的方法可以初始化具有一定大小的数组,而不需要你输入 100 个
|
|
|
元素(但如果你想的话,那当然可以!)。
|
|
|
例如,你可以这样做:
|
|
|
let array = ["Are we there yet?"; 10];
|
|
|
|
|
|
额外目标: 还有哪些东西可以在 `a.len()>=100` 时返回 true """
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "primitive_types4"
|
|
|
path = "exercises/primitive_types/primitive_types4.rs"
|
|
|
mode = "test"
|
|
|
hint = """
|
|
|
看看这本书的:Understanding Ownership -> Slices -> Other Slices 章节吧:
|
|
|
https://doc.rust-lang.org/book/ch04-03-slices.html,
|
|
|
然后找出所需切片元素对应数组里的起始和截止下标。
|
|
|
|
|
|
如果你好奇既然 `assert_eq!` 的第二个参数是引用,为什么第一个参数
|
|
|
没有使用 & 号用来表示引用,可以看看这本书的 Deref 强制转换部分:
|
|
|
https://doc.rust-lang.org/book/ch15-02-deref.html"""
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "primitive_types5"
|
|
|
path = "exercises/primitive_types/primitive_types5.rs"
|
|
|
mode = "compile"
|
|
|
hint = """
|
|
|
看看这本书的 Data Types -> The Tuple Type 类型章节:
|
|
|
https://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type
|
|
|
特别是关于解构的部分(这节中倒数第二个例子)。
|
|
|
你需要一个模式将 `name` 和 `age` 绑定到元组的适当部分。你能够做到的!!"""
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "primitive_types6"
|
|
|
path = "exercises/primitive_types/primitive_types6.rs"
|
|
|
mode = "test"
|
|
|
hint = """
|
|
|
虽然你可以使用 `let` 对元组进行解构 ,但不妨试试对它进行索引,
|
|
|
正如这本书的 Data Types -> The Tuple Type 部分的最后一个例子表示的那样。
|
|
|
https://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type
|
|
|
现在,你的工具箱里又多了一个工具!"""
|
|
|
|
|
|
# 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 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 = """
|
|
|
The new method needs to panic if the weight is physically impossible :), how do we do that in Rust?
|
|
|
|
|
|
For is_international: What makes a package international? Seems related to the places it goes through right?
|
|
|
|
|
|
For calculate_transport_fees: Bigger is more expensive usually, we don't have size, but something may fit the bill here :)
|
|
|
|
|
|
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 = """
|
|
|
Hint: The declaration of the enumeration type has not been defined yet."""
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "enums2"
|
|
|
path = "exercises/enums/enums2.rs"
|
|
|
mode = "compile"
|
|
|
hint = """
|
|
|
Hint: you can create enumerations that have different variants with different types
|
|
|
such as no data, anonymous structs, a single string, tuples, ...etc"""
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "enums3"
|
|
|
path = "exercises/enums/enums3.rs"
|
|
|
mode = "test"
|
|
|
hint = "No hints this time ;)"
|
|
|
|
|
|
# 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."""
|
|
|
|
|
|
# COLLECTIONS
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "vec1"
|
|
|
path = "exercises/collections/vec1.rs"
|
|
|
mode = "test"
|
|
|
hint = """
|
|
|
In Rust, there are two ways to define a Vector.
|
|
|
1. One way is to use the `Vec::new()` function to create a new vector
|
|
|
and fill it with the `push()` method.
|
|
|
2. The second way, which is simpler is to use the `vec![]` macro and
|
|
|
define your elements inside the square brackets.
|
|
|
Check this chapter: https://doc.rust-lang.org/stable/book/ch08-01-vectors.html
|
|
|
of the Rust book to learn more.
|
|
|
"""
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "vec2"
|
|
|
path = "exercises/collections/vec2.rs"
|
|
|
mode = "test"
|
|
|
hint = """
|
|
|
Hint 1: `i` is each element from the Vec as they are being iterated.
|
|
|
Can you try multiplying this?
|
|
|
Hint 2: Check the suggestion from the compiler error ;)
|
|
|
"""
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "hashmap1"
|
|
|
path = "exercises/collections/hashmap1.rs"
|
|
|
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 = "hashmap2"
|
|
|
path = "exercises/collections/hashmap2.rs"
|
|
|
mode = "test"
|
|
|
hint = """
|
|
|
Use the `entry()` and `or_insert()` methods of `HashMap` to achieve this.
|
|
|
Learn more at https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value
|
|
|
"""
|
|
|
|
|
|
# STRINGS
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "strings1"
|
|
|
path = "exercises/strings/strings1.rs"
|
|
|
mode = "compile"
|
|
|
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."""
|
|
|
|
|
|
# TEST 2
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "quiz2"
|
|
|
path = "exercises/quiz2.rs"
|
|
|
mode = "compile"
|
|
|
hint = "No hints this time ;)"
|
|
|
|
|
|
# ERROR HANDLING
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "errors1"
|
|
|
path = "exercises/error_handling/errors1.rs"
|
|
|
mode = "test"
|
|
|
hint = """
|
|
|
`Err` is one of the variants of `Result`, so what the 2nd test is saying
|
|
|
is that `generate_nametag_text` should return a `Result` instead of an
|
|
|
`Option`.
|
|
|
|
|
|
To make this change, you'll need to:
|
|
|
- update the return type in the function signature to be a Result<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]]
|
|
|
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`?"""
|
|
|
|
|
|
[[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 = """
|
|
|
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?
|
|
|
|
|
|
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
|
|
|
|
|
|
This exercise uses some concepts that we won't get to until later in the
|
|
|
course, like `Box` and the `From` trait. It's not important to understand
|
|
|
them in detail right now, but you can read ahead if you like.
|
|
|
|
|
|
Read more about boxing errors:
|
|
|
https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/boxing_errors.html
|
|
|
|
|
|
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
|
|
|
"""
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "generics3"
|
|
|
path = "exercises/generics/generics3.rs"
|
|
|
mode = "test"
|
|
|
hint = """
|
|
|
To find the best solution to this challenge you're going to need to think back to your
|
|
|
knowledge of traits, specifically Trait Bound Syntax - you may also need this: "use std::fmt::Display;"
|
|
|
|
|
|
This is definitely harder than the last two exercises! You need to think about not only making the
|
|
|
ReportCard struct generic, but also the correct property - you will need to change the implementation
|
|
|
of the struct slightly too...you can do it!
|
|
|
"""
|
|
|
|
|
|
# OPTIONS
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "option1"
|
|
|
path = "exercises/option/option1.rs"
|
|
|
mode = "compile"
|
|
|
hint = """
|
|
|
Hint 1: Check out some functions of Option:
|
|
|
is_some
|
|
|
is_none
|
|
|
unwrap
|
|
|
|
|
|
and:
|
|
|
pattern matching
|
|
|
|
|
|
Hint 2: There are no sensible defaults for the value of an Array; the values need to be filled before use.
|
|
|
"""
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "option2"
|
|
|
path = "exercises/option/option2.rs"
|
|
|
mode = "compile"
|
|
|
hint = """
|
|
|
check out:
|
|
|
https://doc.rust-lang.org/rust-by-example/flow_control/if_let.html
|
|
|
https://doc.rust-lang.org/rust-by-example/flow_control/while_let.html
|
|
|
|
|
|
Remember that Options can be stacked in if let and while let.
|
|
|
For example: Some(Some(variable)) = variable2
|
|
|
Also see Option::flatten
|
|
|
"""
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "option3"
|
|
|
path = "exercises/option/option3.rs"
|
|
|
mode = "compile"
|
|
|
hint = """
|
|
|
The compiler says a partial move happened in the `match`
|
|
|
statement. How can this be avoided? The compiler shows the correction
|
|
|
needed. After making the correction as suggested by the compiler, do
|
|
|
read: https://doc.rust-lang.org/std/keyword.ref.html"""
|
|
|
|
|
|
# TRAITS
|
|
|
|
|
|
[[exercises]]
|
|
|
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.
|
|
|
|
|
|
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"""
|
|
|
|
|
|
# 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())`."""
|
|
|
|
|
|
# TEST 3
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "quiz3"
|
|
|
path = "exercises/quiz3.rs"
|
|
|
mode = "test"
|
|
|
hint = "No hints this time ;)"
|
|
|
|
|
|
# STANDARD LIBRARY TYPES
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "box1"
|
|
|
path = "exercises/standard_library_types/box1.rs"
|
|
|
mode = "test"
|
|
|
hint = """
|
|
|
Step 1
|
|
|
The compiler's message should help: since we cannot store the value of the actual type
|
|
|
when working with recursive types, we need to store a reference (pointer) to its value.
|
|
|
We should, therefore, place our `List` inside a `Box`. More details in the book here:
|
|
|
https://doc.rust-lang.org/book/ch15-01-box.html#enabling-recursive-types-with-boxes
|
|
|
|
|
|
Step 2
|
|
|
Creating an empty list should be fairly straightforward (hint: peek at the assertions).
|
|
|
For a non-empty list keep in mind that we want to use our Cons "list builder".
|
|
|
Although the current list is one of integers (i32), feel free to change the definition
|
|
|
and try other types!
|
|
|
"""
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "arc1"
|
|
|
path = "exercises/standard_library_types/arc1.rs"
|
|
|
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 = "iterators1"
|
|
|
path = "exercises/standard_library_types/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 2.1:
|
|
|
Very similar to the lines above and below. You've got this!
|
|
|
Step 3:
|
|
|
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/standard_library_types/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/standard_library_types/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()`."""
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "iterators4"
|
|
|
path = "exercises/standard_library_types/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."""
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "iterators5"
|
|
|
path = "exercises/standard_library_types/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 = """
|
|
|
`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 :)"""
|
|
|
|
|
|
# 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."""
|
|
|
|
|
|
# TEST 4
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "quiz4"
|
|
|
path = "exercises/quiz4.rs"
|
|
|
mode = "test"
|
|
|
hint = "No hints this time ;)"
|
|
|
|
|
|
# CLIPPY
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "clippy1"
|
|
|
path = "exercises/clippy/clippy1.rs"
|
|
|
mode = "clippy"
|
|
|
hint = """
|
|
|
Not every floating point value can be represented exactly in binary values in
|
|
|
memory. Take a look at the description of
|
|
|
https://doc.rust-lang.org/stable/std/primitive.f32.html
|
|
|
When using the binary compare operators with floating points you won't compare
|
|
|
the floating point values but the binary representation in memory. This is
|
|
|
usually not what you would like to do.
|
|
|
See the suggestions of the clippy warning in compile output and use the
|
|
|
machine epsilon value...
|
|
|
https://doc.rust-lang.org/stable/std/primitive.f32.html#associatedconstant.EPSILON"""
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "clippy2"
|
|
|
path = "exercises/clippy/clippy2.rs"
|
|
|
mode = "clippy"
|
|
|
hint = """
|
|
|
`for` loops over Option values are more clearly expressed as an `if let`"""
|
|
|
|
|
|
# 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.
|
|
|
|
|
|
Hint: 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
|
|
|
|
|
|
Hint: 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."""
|
|
|
|
|
|
# ADVANCED ERRORS
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "advanced_errs1"
|
|
|
path = "exercises/advanced_errors/advanced_errs1.rs"
|
|
|
mode = "test"
|
|
|
hint = """
|
|
|
This exercise uses an updated version of the code in errors6. The parsing
|
|
|
code is now in an implementation of the `FromStr` trait. Note that the
|
|
|
parsing code uses `?` directly, without any calls to `map_err()`. There is
|
|
|
one partial implementation of the `From` trait example that you should
|
|
|
complete.
|
|
|
|
|
|
Details: The `?` operator calls `From::from()` on the error type to convert
|
|
|
it to the error type of the return type of the surrounding function.
|
|
|
|
|
|
Hint: You will need to write another implementation of `From` that has a
|
|
|
different input type.
|
|
|
"""
|
|
|
|
|
|
[[exercises]]
|
|
|
name = "advanced_errs2"
|
|
|
path = "exercises/advanced_errors/advanced_errs2.rs"
|
|
|
mode = "test"
|
|
|
hint = """
|
|
|
This exercise demonstrates a few traits that are useful for custom error
|
|
|
types to implement. These traits make it easier for other code to consume
|
|
|
the custom error type.
|
|
|
|
|
|
Follow the steps in the comment near the top of the file. You will have to
|
|
|
supply a missing trait implementation, and complete a few incomplete ones.
|
|
|
|
|
|
You may find these pages to be helpful references:
|
|
|
https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/define_error_type.html
|
|
|
https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/boxing_errors.html
|
|
|
https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/wrap_error.html
|
|
|
|
|
|
Hint: What trait must our error type have for `main()` to return the return
|
|
|
type that it returns?
|
|
|
|
|
|
Another hint: It's not necessary to implement any methods inside the missing
|
|
|
trait. (Some methods have default implementations that are supplied by the
|
|
|
trait.)
|
|
|
|
|
|
Another hint: Consult the tests to determine which error variants (and which
|
|
|
error message text) to produce for certain error conditions.
|
|
|
|
|
|
Challenge: There is one test that is marked `#[ignore]`. Can you supply the
|
|
|
missing code that will make it pass? You may want to consult the standard
|
|
|
library documentation for a certain trait for more hints.
|
|
|
"""
|