# 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` 变量。 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 不只有一种结构。实际上,所有的变体都是用来组合相关的数据。 首先是一般的(或经典的)结构,一块相关的数据被命名为一个字段集合。 元组结构基本上就是被命名的元组。 最后的单元结构没有任何字段,对泛型很有用。 在这个练习中,你需要完成并实现每一种结构。 更多关于结构的内容在这:https://doc.rust-lang.org/book/ch05-01-defining-structs.html""" [[exercises]] name = "structs2" path = "exercises/structs/structs2.rs" mode = "test" hint = """ 创建结构体的实例很简单,你只需要给它的字段分配一些值。 然而,在实例化结构时,还有些捷径。 看看这本书,来了解更多: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 = """ Package 实现的 new 方法在重量(weight_in_grams)不符合物理的情况下需要 panic :),这在 Rust 需要怎么做? 对于 is_international:一个包裹具有国际性的条件有哪些?似乎与它所经过的地方有关吧? 对于 calculate_transport_fees:更大的通常更贵,我们的 Package 没有尺寸,但有些东西可能同样符合需要 :) 看看这本书,了解更多关于方法实现的信息:https://doc.rust-lang.org/book/ch05-03-method-syntax.html""" # ENUMS [[exercises]] name = "enums1" path = "exercises/enums/enums1.rs" mode = "compile" hint = """ 提示:枚举类型还没有被定义。""" [[exercises]] name = "enums2" path = "exercises/enums/enums2.rs" mode = "compile" hint = """ 提示:你可以创建多种不同类型的枚举,如不包含值、匿名结构*、字符串、元组,等等。 译:匿名结构可能是不命名字段名的结构""" [[exercises]] name = "enums3" path = "exercises/enums/enums3.rs" mode = "test" hint = "这次没有提示 ;)" # MODULES [[exercises]] name = "modules1" path = "exercises/modules/modules1.rs" mode = "compile" hint = """ Rust 中所有的东西默认都是私有的(private)——但是有个关键字可以标明某些东西为公开的(public)。 而编译器错误正指明某些东西需要能够被公开访问""" [[exercises]] name = "modules2" path = "exercises/modules/modules2.rs" mode = "compile" hint = """ delicious_snacks 模块试图提供一个区别其内部结构的外部接口(对于 `fruits` 和 `veggies` 以及相关的常数)。 参照 main 中的使用情况完善 `use` 语句,并找到两个常量中缺少的某个关键字。""" [[exercises]] name = "modules3" path = "exercises/modules/modules3.rs" mode = "compile" hint = """ UNIX_EPOCH 和 SystemTime 声明在 std::time 模块。通过 `use` 语句 将它们引入作用域。你可以使用嵌套路径(nested paths) 或全局操作符(glob operator)只需一行就能够引入它们。 译:嵌套是 {x,y,z} ,全局是 * """ # COLLECTIONS [[exercises]] name = "vec1" path = "exercises/collections/vec1.rs" mode = "test" hint = """ Rust 有以下两种方式定义一个 vector 。 1. 一种方法是使用 `Vec::new()` 来创建一个新的 vector,然后 使用 `push()` 方法往里面添加元素。 2. 第二种方法更简单,那就是使用 `vec![]` 宏,接着在宏的方括号内定义需要的元素。 阅读这本书的此章节:https://doc.rust-lang.org/stable/book/ch08-01-vectors.html 来了解更多。 """ [[exercises]] name = "vec2" path = "exercises/collections/vec2.rs" mode = "test" hint = """ 提示 1 :`i` 是 Vec 中被迭代的每个元素,你可以乘以它吗? 提示 2 : 研究下编译错误给出的建议 ;) """ [[exercises]] name = "hashmap1" path = "exercises/collections/hashmap1.rs" mode = "test" hint = """ 提示 1 :看看函数的返回类型,推测出 `basket` 的类型。 提示 2 :水果的数量应该不少于 5 个。并且需要确保篮子里至少有三种水果。 """ [[exercises]] name = "hashmap2" path = "exercises/collections/hashmap2.rs" mode = "test" hint = """ 使用 `HashMap` 中的 `entry()` 和 `or_insert()` 来完成目标。 了解更多: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 = """ `current_favorite_color` 函数返回的是一个生命周期为 `'static` 的字符串切片。 我们知道这点是因为字符串直接存储在了代码中——它并不来自于文件、用户输入或 其他程序——所以只要程序还在运行,它就会一直存在。但它仍然是一个字符串切片。 有一种方法可以将字符串切片转换为 `String`,这在书的 Strings 章节有所介绍,还有一种 方法是使用 `From` 特征""" [[exercises]] name = "strings2" path = "exercises/strings/strings2.rs" mode = "compile" hint = """ 是的,只要把绑定在 `word` 上的值改为字符串切片而非 `String` 就可以很容易地解决这个问题,不是吗?? 有个方法是在第 9 行添一个字符,这将强制把 `String` 转为字符串切片。""" # 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` 是 `Result` 的成员之一,所以第二个测试的意思是 `generate_nametag_text` 应该返回 `Result` 而不是 `Option` 。 要做到这些改变,你需要: - 修改函数签名的返回类型为 Result,以便能返回 `Ok(String)` 和 `Err(String)`。 - 更改函数返回值 `Some(stuff)` 为 `Ok(stuff)` 。 - 更改函数返回值 `None` 为 `Err(error message)` 。 - 将第一个测试预期的值从 `Some(stuff)` 改为 `Ok(stuff)`。""" [[exercises]] name = "errors2" path = "exercises/error_handling/errors2.rs" mode = "test" hint = """ 解决这个问题的一个方法是对 `item_quantity.parse::()` 使用 match 语句, 其中有两种情况需要被处理,分别是 `Ok(something)` 和 `Err(something)`。 不过这种方法在 Rust 中很常见,所以有一个 `?` 操作符,作用几乎符合你想让匹配语句做的事! 看一下 Error Handling 章节的这部分: https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator 然后试一试!""" [[exercises]] name = "errors3" path = "exercises/error_handling/errors3.rs" mode = "compile" hint = """ 如果其它函数可以返回 `Result`,为什么 `main` 函数不能?""" [[exercises]] name = "errors4" path = "exercises/error_handling/errors4.rs" mode = "test" hint = """ `PositiveNonzeroInteger::new` 将创建一个新的实例,并返回一个 `Ok` 。 它应该做一些检查,如果检查到失败,则返回 `Err` ,如果确定一切正常,则返回 `Ok` :)。""" [[exercises]] name = "errors5" path = "exercises/error_handling/errors5.rs" mode = "compile" hint = """ 提示:在 `main()` 中产生了两种 `Result` 类型,它们是通过 `?` 运算符返回的。 那么我们如何在 `main()` 中声明一个容纳这两者的返回类型? 额外提示:`?` 操作符的底层实现实际上是对错误值调用了 `From::from` ,将其转换为 了 `Box` 类型。它是多态的——这意味着不同类型的错误可以从同一个函数返回, 因为它们都实现了`error::Error`特征,行为都是一致的。 请看这本书的这一部分: https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator 这个练习使用了一些课程后期才会介绍到的概念,如 `Box` 指针和 `From` 特征。 现在详细了解它们并不重要,但如果你感兴趣,可以提前阅读。 阅读更多装箱错误(boxing errors)的内容: 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/reenter_question_mark.html """ [[exercises]] name = "errors6" path = "exercises/error_handling/errors6.rs" mode = "test" hint = """ 这个练习使用的是来自于 error 4 的 `PositiveNonzeroInteger` 完整版本。 在 TODO 要求你修改的那一行下面,有一个在 `Result` 上使用 `map_err()` 方法将 一种类型的错误转换为另一种类型的例子。尝试在 `parse()` 的 `Result` 上使用类似的东西。 你可能使用 `?` 操作符在函数中提前返回,或者使用 `match` 表达式,以及等等其它方法。 你可以在 `impl ParsePosNonzeroError` 内创建另一个方法来配合 `map_err()` 使用。 在 `std::result` 文档了解更多关于 `map_err()` 的信息: 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 = """ Rust 中的 Vector 通过泛型来创建任意类型的动态数组。 所以你要告诉编译器,我们要把什么类型的元素推入到这个数组里。""" [[exercises]] name = "generics2" path = "exercises/generics/generics2.rs" mode = "test" hint = """ 目前我们只包装了 'u32' 类型的值。 或许我们可以通过某种方式来修改对类型的显式引用? 如果你还没有头绪: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 = """ 为了找到解决这个难题的最佳方法,你需要回想一下有关 trait 的知识,特别是 trait 的绑定语法——可能 还需要这个:"use std::fmt::Display;" 这绝对比前两个练习更难!你需要使 ReportCard 结构通用,并拥有正确的属性,同时 你也需要稍微改变一下结构的实现……加油💪! """ # OPTIONS [[exercises]] name = "option1" path = "exercises/option/option1.rs" mode = "compile" hint = """ 提示 1:了解 Option 的一些方法: is_some is_none unwrap 和: 模式匹配 提示 2:数组没有合适的默认值;在使用前需要添加这些值。 """ [[exercises]] name = "option2" path = "exercises/option/option2.rs" mode = "compile" hint = """ 查看: 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 记住,可以多次层叠地对 Options 使用 if let 或 while let 。 例如: Some(Some(variable)) = variable2 也可以参考下 Option::flatten """ [[exercises]] name = "option3" path = "exercises/option/option3.rs" mode = "compile" hint = """ 编译器说 `match` 语句中发生了部分移动。 怎样避免这种情况?编译器也已给出了所需的修正。 按照编译器的建议进行修正后,请阅读:https://doc.rust-lang.org/std/keyword.ref.html""" # TRAITS [[exercises]] name = "traits1" path = "exercises/traits/traits1.rs" mode = "test" hint = """ 下面的网站是关于 Rust 中 Traits 的讨论: https://doc.rust-lang.org/book/ch10-02-traits.html """ [[exercises]] name = "traits2" path = "exercises/traits/traits2.rs" mode = "test" hint = """ 注意 trait 是如何取得 'self' 的所有权,并返回一个 'Self' 的。 尝试修改传入的字符串 vector。 Vector 已提供了在它尾部添加一个元素的方法。 参阅文档:https://doc.rust-lang.org/std/vec/struct.Vec.html""" # TESTS [[exercises]] name = "tests1" path = "exercises/tests/tests1.rs" mode = "test" hint = """ 你甚至不需要写任何用于测试的代码——直接填入某个值然后运行,即使在现实中这样做没什么意义 :) `assert!` 是需要一个参数的宏。根据参数的值,`assert!` 可能什么也不会发生(这意味将通 过测试)或者 `assert!` 引发了 panic (测试失败)。所以试着去给 `assert!` 赋予不同的值,看 看哪些可以编译,哪些能够通过,哪些将造成失败 :)""" [[exercises]] name = "tests2" path = "exercises/tests/tests2.rs" mode = "test" hint = """ 和前面的练习一样,你无需编写任何代码就可以编译和运行这个测试。 `assert_eq!` 是一个接受两个参数并比较两者的宏。尝试给它两个相等、两个不同的 值和两个不同类型的值!也不妨试试调换下两个参数的位置""" [[exercises]] name = "tests3" path = "exercises/tests/tests3.rs" mode = "test" hint = """ 你可以在 `assert!` 接受参数的位置直接调用一个函数——所以你可以做一些类似于 `assert!(having_fun())` 的事情。如果你想检查得到的值是不是 false ,可以 用 `!` 来取反结果,例如 `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 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::`. 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 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. """