Merge remote-tracking branch 'upstream/main'

pull/123/head
lijinpeng 3 years ago
commit 0a09bb6b3e

@ -66,7 +66,7 @@
- [格式化输出](advance/formatted-output.md)
- [智能指针 doing](advance/smart-pointer/intro.md)
- [Box<T>堆对象分配](advance/smart-pointer/box.md)
- [Deref和Drop特征(todo)](advance/smart-pointer/deref-drop.md)
- [Deref解引用](advance/smart-pointer/deref.md)
- [Cell todo](advance/smart-pointer/cell.md)
- [Rc与RefCell(todo)](advance/smart-pointer/rc-refcell.md)
- [自引用与内存泄漏(todo)](advance/smart-pointer/self-referrence.md)

@ -0,0 +1,297 @@
# Deref解引用
智能指针的名称来源,主要就在于它实现了`Deref`和`Drop`特征,这两个特征可以智能地帮助我们节省使用上的负担:
- `Deref`可以让智能指针像引用那样工作,这样你就就可以写出同时支持智能指针和引用的代码, 例如`&T`
- `Drop`允许你指定智能指针超出作用域后自动执行的代码,例如做一些数据清除等收尾工作
下面先来看看`Deref`特征是如何工作的。
## 通过`*`获取引用背后的值
在正式讲解`Deref`之前,我们先来看下常规引用的解引用。
常规引用是一个指针类型,包含了目标数据存储的内存地址。对常规引用使用`*`操作符,就可以通过解引用的方式获取到内存地址对应的数据值:
```rust
fn main() {
let x = 5;
let y = &x;
assert_eq!(5, x);
assert_eq!(5, *y);
}
```
这里`y`就是一个常规引用,包含了值`5`所在的内存地址, 然后通过解引用`*y`,我们获取到了值`5`。如果你试图执行`assert_eq!(5, y);`,代码就会无情报错,因为你无法将一个引用与一个数值做比较:
```console
error[E0277]: can't compare `{integer}` with `&{integer}` //无法将{integer} 与&{integer}进行比较
--> src/main.rs:6:5
|
6 | assert_eq!(5, y);
| ^^^^^^^^^^^^^^^^^ no implementation for `{integer} == &{integer}`
|
= help: the trait `PartialEq<&{integer}>` is not implemented for `{integer}`
// 你需要为{integer}实现用于比较的特征PartialEq<&{integer}>
```
## 智能指针解引用
上面所说的解引用方式和其它大多数语言并无区别但是Rust中将解引用提升到了一个新高度。考虑一下智能指针它是一个结构体类型如果你直接对它进行`*myStruct`,显然编译器不知道该如何办,因此我们可以为智能指针结构体实现`Deref`特征。
实现`Deref`后的智能指针结构体,就可以像普通引用一样,通过`*`进行解引用,例如`Box<T>`智能指针:
```rust
fn main() {
let x = Box::new(1);
let sum = *x + 1;
}
```
智能指针`x`被`*`解引用为`i32`类型的值`1`,然后再进行求和。
#### 定义自己的智能指针
现在,让我们一起来实现一个智能指针,功能上类似`Box<T>`。由于`Box<T>`本身很简单,并没有包含类如长度、最大长度等信息,因此用一个元组结构体即可。
```rust
struct MyBox<T>(T);
impl<T> MyBox<T> {
fn new(x: T) -> MyBox<T> {
MyBox(x)
}
}
```
跟`Box<T>`一样,我们的智能指针也持有一个`T`类型的值,然后使用关联函数`MyBox::new`来创建智能指针。由于还未实现`Deref`特征,此时使用`*`肯定会报错:
```rust
fn main() {
let y = MyBox::new(5);
assert_eq!(5, *y);
}
```
运行后,报错如下:
```console
error[E0614]: type `MyBox<{integer}>` cannot be dereferenced
--> src/main.rs:12:19
|
12 | assert_eq!(5, *y);
| ^^
```
##### 为智能指针实现Deref特征
现在来为`MyBox`实现`Deref`特征, 以支持`*`解引用操作符:
```rust
use std::ops::Deref;
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
```
很简单,当解引用`MyBox`智能指针时,返回元组结构体中的元素`&self.0`, 有几点要注意的:
- 为了可读性, 我们声明了关联类型`Target`
- `deref`返回的是一个常规引用,可以被`*`进行解引用
之前报错的代码此时已能顺利编译通过。当然,标准库实现的智能指针要考虑很多边边角角情况,肯定比我们的实现要复杂。
## `*`背后的原理
当我们对智能指针`Box`进行解引用时, 实际上Rust为我们调用了以下方法
```rust
*(y.deref())
```
首先调用`deref`方法返回值的常规引用,然后通过`*`对常规引用进行解引用,最终获取到目标值。
至于Rust为何要使用这个有点啰嗦的方式实现原因是因为所有权系统的存在。如果`deref`方法直接返回一个值,而不是引用,那么该值的所有权将被转移给调用者,而我们不希望调用者仅仅只是`*T`一下,就拿走了智能指针中包含的值。
需要注意的是,`*`不会无限递归替换,从`*y`到`*(y.deref())`只会发生一次,而不会继续进行替换然后产生形如`*((y.deref()).deref())`的怪物。
## 函数和方法中的隐式Deref转换
在函数和方法中Rust提供了一个极其有用的隐式转换`Deref`转换。简单来说,当一个实现了`Deref`特征的值被传给函数或方法时,会根据函数参数的要求,来决定使用该值原本的类型还是`Deref`后的类型,例如:
```rust
fn main() {
let s = String::from("hello world");
display(&s)
}
fn display(s: &str) {
println!("{}",s);
}
```
以上代码有几点值得注意:
- `String`实现了`Deref`特征,能被转换成一个`&str`
- `s`是一个`String`类型,当它被传给`display`函数时,自动通过`Deref`转换成了`&str`
- 必须使用`&s`的方式来触发`Deref`
#### 连续的隐式Deref转换
如果你以为`Deref`仅仅这点作用,那就大错特错了。`Deref`可以支持连续的隐式转换,直到找到适合的形式为止:
```rust
fn main() {
let s = MyBox::new(String::from("hello world"));
display(&s)
}
fn display(s: &str) {
println!("{}",s);
}
```
这里我们使用了之前自定义的智能指针`MyBox`,并将其通过连续的隐式转换变成`&str`类型:首先`MyBox`被`Deref`成`String`类型,结果并不能满足`display`函数参数的要求,编译器发现`String`还可以继续`Deref`成`&str`,最终成功的匹配了函数参数。
想象一下,假如`Rust`没有提供这种隐式转换,我们该如何调用`display`函数?
```rust
fn main() {
let m = MyBox::new(String::from("Rust"));
hello(&(*m)[..]);
}
```
结果不言而喻,肯定是`&s`的方式优秀的多。总之,当参与其中的类型定义了`Deref`特征时Rust会分析该类型并且连续使用`Deref`直到最终获得一个引用来匹配函数或者方法的参数类型,这种行为完全不会造成任何的性能损耗, 因为完全是在编译期完成。
但是`Deref`并不是没有缺点,缺点就是:如果你不知道某个类型实现了`Deref`特征,那么在看到某段代码时,并不能在第一时间反应过来该代码发生了隐式的`Deref`转换。事实上,不仅仅是`Deref`在Rust中还有各种`From/Into`等等会给阅读代码带来一定负担的特征。还是那句话一切选择都是权衡有得必有失得了代码的简洁性往往就失去了可读性Go语言就是一个刚好相反的例子。
再来看一下在方法、赋值中自动应用`Deref`的例子:
```rust
fn main() {
let s = MyBox::new(String::from("hello, world"));
let s1:&str = &s;
let s2: String = s.to_string();
}
```
对于`s1`,我们通过两次`Deref`将`&str`类型的值赋给了它;而对于`s2`,我们在其上直接调用方法`to_string`, 实际上`MyBox`根本没有没有实现该方法,能调用`to_string`,完全是因为编译器对`MyBox`应用了`Deref`的结果。
## Deref规则总结
在上面,我们零碎的介绍了不少关于`Deref`特征的知识,下面来通过较为正式的方式来对其规则进行下总结。
一个类型为`T`的对象`foo`,如果`T: Deref<Target=U>`,那么,相关`foo`的引用`&foo`在应用的时候会自动转换`&U`。
粗看这条规则,貌似有点类似于`AsRef`,而跟`解引`似乎风马牛不相及, 实际里面里面有些玄妙之处。
Rust编译器会在做`*v`操作的时候,自动先把`v`做引用归一化操作,即转换成内部通用引用的形式`&v`,整个表达式就变成 `*&v`。这里面有两种情况:
1. 把智能指针比如在库中定义的Box, Rc, Arc, Cow 等),去掉壳,转成内部标准形式`&v`
2. 把多重`&` (比如:`&&&&&&&v`),简化成`&v`(通过插入足够数量的`*`进行解引)。
所以,它实际上在解引用之前做了一个引用的归一化操作。
为什么要转呢? 因为编译器设计的能力是,只能够对 &v 这种引用进行解引用。其它形式的它不认识,所以要做引用归一化操作。
使用引用进行过渡也是为了能够防止不必要的拷贝。
下面举一些例子:
```rust
fn foo(s: &str) {
// borrow a string for a second
}
// String implements Deref<Target=str>
let owned = "Hello".to_string();
// therefore, this works:
foo(&owned);
```
因为`String`实现了`Deref<Target=str>`。
```rust
use std::rc::Rc;
fn foo(s: &str) {
// borrow a string for a second
}
// String implements Deref<Target=str>
let owned = "Hello".to_string();
let counted = Rc::new(owned);
// therefore, this works:
foo(&counted);
```
因为`Vec<T>` 实现了`Deref<Target=[T]>`。
```rust
struct Foo;
impl Foo {
fn foo(&self) { println!("Foo"); }
}
let f = &&Foo;
f.foo();
(&f).foo();
(&&f).foo();
(&&&&&&&&f).foo();
```
## 三种Deref转换
在之前,我们讲的都是不可变的`Deref`转换实际上Rust还支持将一个可变的引用转换成另一个可变的引用以及将一个可变引用转换成不可变的引用规则如下
- 当`T: Deref<Target=U>`,可以将`&T`转换成`&U`,也就是我们之前看到的例子
- 当`T: DerefMut<Target=U>`,可以将`&mut T`转换成`&mut U`
- 当`T: Deref<Target=U>`,可以将`&mut T`转换成`&U`
来看一个关于`DerefMut`的例子:
```rust
struct MyBox<T> {
v: T
}
impl<T> MyBox<T> {
fn new(x: T) -> MyBox<T> {
MyBox{
v: x
}
}
}
use std::ops::Deref;
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.v
}
}
use std::ops::DerefMut;
impl<T> DerefMut for MyBox<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.v
}
}
fn main() {
let mut s = MyBox::new(String::from("hello, "));
display(&mut s)
}
fn display(s: &mut String) {
s.push_str("world");
println!("{}",s);
}
```
以上代码有几点值得注意:
- 要实现`DerefMut`必须要先实现`Deref`特征: `pub trait DerefMut: Deref {`
- `T: DerefMut<Target=U>`解读:将`&mut T`类型通过`DerefMut`特征的方法转换为`&mut U`类型,对应上例中,就是将`&mut MyBox<String>`转换为`&mut String`
对于上述三条规则中的第三条它比另外两条稍微复杂了点Rust可以把可变引用隐式的转换成不可变引用但反之则不行。
如果从Rust的所有权和借用规则的角度考虑当你拥有一个可变的引用那该引用肯定是对应数据的唯一借用那么此时将可变引用变成不可变引用并不会破坏借用规则但是如果你拥有一个不可变引用那同时可能还存在其它几个不可变的引用如果此时将其中一个不可变引用转换成可变引用就变成了可变引用与不可变引用的共存最终破坏了借用规则。
## 总结
`Deref`可以说是Rust中最常见的隐式类型转换而且它可以连续的实现如`Box<String> -> String -> &str`的隐式转换,只要链条上的类型实现了`Deref`特征。
我们也可以为自己的类型实现`Deref`特征, 但是原则上来说,只应该为自定义的智能指针实现`Deref`。例如,虽然你可以为自己的自定义数组类型实现`Deref`以避免`myArr.0[0]`的使用形式但是Rust官方并不推荐这么做特别是在你开发三方库时。

@ -285,7 +285,7 @@ use num::complex::Complex;
- **Rust拥有相当多的数值类型**. 因此你需要熟悉这些类型所占用的字节数,这样就知道该类型允许的大小范围以及你选择的类型是否能表达负数
- **类型转换必须是显式的**. Rust永远也不会偷偷把你的16bit整数转换成32bit整数
- **Rust的数值上可以使用方法**. 例如你可以用以下方法来将`24.5`取整: `13.14_f32.round()`, 在这里我们使用了类型后缀,因为编译器需要知道`13.14`的具体类型
- **Rust的数值上可以使用方法**. 例如你可以用以下方法来将`13.14`取整: `13.14_f32.round()`, 在这里我们使用了类型后缀,因为编译器需要知道`13.14`的具体类型
数值类型的讲解已经基本结束,接下来来看看字符和布尔类型。

@ -1,6 +1,6 @@
# 结构体
上一节中提到需要一个更高级的数据结构来帮助我们更好的抽象问题,结构体`strct`恰恰就是这样的复合数据结构,它是由其它数据类型组合而来。 其它语言也有类似的数据结构,不过可能有不同的名称,例如`object`、`record`等。
上一节中提到需要一个更高级的数据结构来帮助我们更好的抽象问题,结构体`struct`恰恰就是这样的复合数据结构,它是由其它数据类型组合而来。 其它语言也有类似的数据结构,不过可能有不同的名称,例如`object`、`record`等。
结构体跟之前讲过的[元组](./tuple.md)有些相像:都是由多种类型组合而成。但是与元组不同的是,结构体可以为内部的每个字段起一个富有含义的名称。因此结构体更加灵活更加强大,你无需依赖这些字段的顺序来访问和解析它们。
@ -107,7 +107,7 @@ fn build_user(email: String, username: String) -> User {
> 仔细回想一下[所有权](../ownership/ownership.md#拷贝(浅拷贝))那一节的内容我们提到了Copy特征实现了Copy特征的类型无需所有权转移可以直接在赋值时进行
> 数据拷贝,其中`bool`和`u64`类型就实现了`Copy`特征,因此`active`和`sign_in_count`字段在赋值给user2时仅仅发生了拷贝而不是所有权转移.
>
> 值的注意的是:`username`所有权被转移给了`user2`,导致了`user1`无法再被使用,但是并不代表`user1`内部的字段不能被继续使用,例如:
> 值的注意的是:`username`所有权被转移给了`user2`,导致了`user1`无法再被使用,但是并不代表`user1`内部的其它字段不能被继续使用,例如:
```rust
let user1 = User {

@ -251,7 +251,7 @@ impl<T> Clone for Container<T> {
前方危险,敬请绕行!
类型系统,你让开!我要自己转换这些类型,不成功便成仁!虽然本书都是关于非安全的内容,我还是希望你能仔细考虑避免使用本章讲到的内容。这是你在 Rust 中所能做到的真真正正、彻彻底底、最最可怕的非安全行为, 在这里,所有的保护机制都形同虚设。
类型系统,你让开!我要自己转换这些类型,不成功便成仁!虽然本书大多是关于安全的内容,我还是希望你能仔细考虑避免使用本章讲到的内容。这是你在 Rust 中所能做到的真真正正、彻彻底底、最最可怕的非安全行为, 在这里,所有的保护机制都形同虚设。
先让你看看深渊长什么样,开开眼,然后你再决定是否深入: `mem::transmute<T, U>`将类型`T`直接转成类型`U`,唯一的要求就是,这两个类型占用同样大小的字节数!我的天,这也算限制?这简直就是无底线的转换好吧?看看会导致什么问题:
1. 首先也是最重要的,转换后创建一个任意类型的实例会造成无法想象的混乱,而且根本无法预测。不要把`3`转换成`bool`类型,就算你根本不会去使用该`bool`类型,也不要去这样转换。

@ -278,7 +278,7 @@ enum MyEnum {
}
fn main() {
let v = vec![MyEnum::Foo,MyEnum::Bar,MyEnum::F
let v = vec![MyEnum::Foo,MyEnum::Bar,MyEnum::Foo];
}
```
@ -287,7 +287,7 @@ fn main() {
v.iter().filter(|x| x == MyEnum::Foo);
```
但是,实际上这行代码会保存,因为你无法将`x`跟一个类型进行比较。好在,你可以使用`match`来完成,但是会导致代码更为啰嗦,是否有更简洁的方式?答案是使用`matches!`:
但是,实际上这行代码会报错,因为你无法将`x`跟一个类型进行比较。好在,你可以使用`match`来完成,但是会导致代码更为啰嗦,是否有更简洁的方式?答案是使用`matches!`:
```rust
v.iter().filter(|x| matches!(x, MyEnum::Foo));
```

@ -1,7 +1,9 @@
# Functions
# 函数(Functions
Here, you'll learn how to write functions and how Rust's compiler can trace things way back.
在本练习,你将学习如何编写一个函数,以及 Rust 编译器怎样可以对事物进行追溯trace things way back
## Further information
译:依据练习的内容,追溯的意思可能是类型推导之类的事。
## 更多信息
- [How Functions Work](https://doc.rust-lang.org/book/ch03-03-how-functions-work.html)

@ -1,8 +1,9 @@
// functions1.rs
// Make me compile! Execute `rustlings hint functions1` for hints :)
// 让我能够编译!执行 `rustex hint functions1` 获取提示 :)
// I AM NOT DONE
/// 翻译: [mg-chao](https://github.com/mg-chao)
fn main() {
call_me();
}

@ -1,14 +1,15 @@
// functions2.rs
// Make me compile! Execute `rustlings hint functions2` for hints :)
// 让我能够编译!执行 `rustex hint functions2` 获取提示 :)
// I AM NOT DONE
/// 翻译: [mg-chao](https://github.com/mg-chao)
fn main() {
call_me(3);
}
fn call_me(num:) {
for i in 0..num {
println!("Ring! Call number {}", i + 1);
println!("Ring! Call number {}", i + 1);// 译:"叮!呼叫号码 {}"
}
}

@ -1,8 +1,9 @@
// functions3.rs
// Make me compile! Execute `rustlings hint functions3` for hints :)
// 让我能够编译!执行 `rustex hint functions3` 获取提示 :)
// I AM NOT DONE
/// 翻译: [mg-chao](https://github.com/mg-chao)
fn main() {
call_me();
}

@ -1,14 +1,15 @@
// functions4.rs
// Make me compile! Execute `rustlings hint functions4` for hints :)
// 让我能够编译!执行 `rustex hint functions4` 获取提示 :)
// This store is having a sale where if the price is an even number, you get
// 10 Rustbucks off, but if it's an odd number, it's 3 Rustbucks off.
// 商店正在进行促销,如果价格是偶数,可以优惠 10 Rustbucks如果是奇数则优惠 3 Rustbucks。
// Rustbucks 可能想表达 Rust元 的意思,好比 美元 。
// I AM NOT DONE
/// 翻译: [mg-chao](https://github.com/mg-chao)
fn main() {
let original_price = 51;
println!("Your sale price is {}", sale_price(original_price));
println!("Your sale price is {}", sale_price(original_price));// 译:"你需支付 {}"
}
fn sale_price(price: i32) -> {

@ -1,11 +1,12 @@
// functions5.rs
// Make me compile! Execute `rustlings hint functions5` for hints :)
// 让我能够编译!执行 `rustex hint functions5` 获取提示 :)
// I AM NOT DONE
/// 翻译: [mg-chao](https://github.com/mg-chao)
fn main() {
let answer = square(3);
println!("The answer is {}", answer);
println!("The answer is {}", answer);// 译:"答案是 {}"
}
fn square(num: i32) -> i32 {

@ -1,7 +1,7 @@
# If
`if`, the most basic type of control flow, is what you'll learn here.
你将在这学习最基本的控制流control flow——`if`
## Further information
## 更多信息
- [Control Flow - if expressions](https://doc.rust-lang.org/book/ch03-05-control-flow.html#if-expressions)

@ -3,14 +3,14 @@
// I AM NOT DONE
pub fn bigger(a: i32, b: i32) -> i32 {
// Complete this function to return the bigger number!
// Do not use:
// - another function call
// - additional variables
// Execute `rustlings hint if1` for hints
// 完成这个返回更大数字的函数!
// 但不允许以下方式:
// - 调用其它函数
// - 额外变量
// 执行 `rustex hint if1` 获取提示
}
// Don't mind this for now :)
// 暂时不要在意它 :)
#[cfg(test)]
mod tests {
use super::*;

@ -1,8 +1,8 @@
// if2.rs
// Step 1: Make me compile!
// Step 2: Get the bar_for_fuzz and default_to_baz tests passing!
// Execute the command `rustlings hint if2` if you want a hint :)
// 第一步:让我能够编译!
// 第二步bar_for_fuzz 和 default_to_baz 可以通过测试!
// 执行 `rustex hint if2` 获取提示 :)
// I AM NOT DONE
@ -14,7 +14,7 @@ pub fn fizz_if_foo(fizzish: &str) -> &str {
}
}
// No test changes needed!
// 测试不需要更改。
#[cfg(test)]
mod tests {
use super::*;

@ -1,9 +1,9 @@
# Variables
# 变量(Variables
In Rust, variables are immutable by default.
When a variable is immutable, once a value is bound to a name, you cant change that value.
You can make them mutable by adding mut in front of the variable name.
在 Rust变量默认是不可变的.
不可变意味着当一个值被绑定到某个名字上,你就不能再对这个值做出更改。
当然,你可以通过在变量名前添加 mut 来使它们变得可变。
## Further information
## 更多信息
- [Variables and Mutability](https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html)

@ -12,5 +12,5 @@
/// 加油 💪
fn main() {
x = 5;
println!("x has the value {}", x);
println!("x has the value {}", x);// 译x 的值是
}

@ -1,13 +1,14 @@
// variables2.rs
// Make me compile! Execute the command `rustlings hint variables2` if you want a hint :)
// 让我能够编译!执行 `rustex hint variables2` 获取提示 :)
// I AM NOT DONE
/// 翻译: [mg-chao](https://github.com/mg-chao)
fn main() {
let x;
if x == 10 {
println!("Ten!");
println!("Ten!");// 译:十!
} else {
println!("Not ten!");
println!("Not ten!");// 译:不是十!
}
}

@ -1,11 +1,12 @@
// variables3.rs
// Make me compile! Execute the command `rustlings hint variables3` if you want a hint :)
// 让我能够编译!执行 `rustex hint variables3` 获取提示 :)
// I AM NOT DONE
/// 翻译: [mg-chao](https://github.com/mg-chao)
fn main() {
let x = 3;
println!("Number {}", x);
x = 5; // don't change this line
println!("Number {}", x);// 译:"数字 {}"
x = 5; // don't change this line(译:不要更改这一行)
println!("Number {}", x);
}

@ -1,8 +1,9 @@
// variables4.rs
// Make me compile! Execute the command `rustlings hint variables4` if you want a hint :)
// 让我能够编译!执行 `rustex hint variables4` 获取提示 :)
// I AM NOT DONE
/// 翻译: [mg-chao](https://github.com/mg-chao)
fn main() {
let x: i32;
println!("Number {}", x);

@ -1,11 +1,12 @@
// variables5.rs
// Make me compile! Execute the command `rustlings hint variables5` if you want a hint :)
// 让我能够编译!执行 `rustex hint variables5` 获取提示 :)
// I AM NOT DONE
/// 翻译: [mg-chao](https://github.com/mg-chao)
fn main() {
let number = "T-H-R-E-E"; // don't change this line
println!("Spell a Number : {}", number);
println!("Spell a Number : {}", number);// 译:"拼接的数字:{}"
number = 3;
println!("Number plus two is : {}", number + 2);
println!("Number plus two is : {}", number + 2);// 译:"数字加上二是:{}"
}

@ -1,8 +1,9 @@
// variables6.rs
// Make me compile! Execute the command `rustlings hint variables6` if you want a hint :)
// 让我能够编译!执行 `rustex hint variables6` 获取提示 :)
// I AM NOT DONE
/// 翻译: [mg-chao](https://github.com/mg-chao)
const NUMBER = 3;
fn main() {
println!("Number {}", NUMBER);

@ -13,62 +13,57 @@ 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?"""
Rust `x` .
7
x
x
x 10 """
[[exercises]]
name = "variables3"
path = "exercises/variables/variables3.rs"
mode = "compile"
hint = """
In Rust, variable bindings are immutable by default. But here we're trying
to reassign a different value to x! There's a keyword we can use to make
a variable binding mutable instead."""
Rust
x 使使"""
[[exercises]]
name = "variables4"
path = "exercises/variables/variables4.rs"
mode = "compile"
hint = """
Oops! In this exercise, we have a variable binding that we've created on
line 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!"""
7 8
使 x
Bug Rust """
[[exercises]]
name = "variables5"
path = "exercises/variables/variables5.rs"
mode = "compile"
hint = """
In variables3 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':
variables3 使使
使
Rust Shadowing
'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."""
使
Variables and Mutability"""
[[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.
Constant
'const' 'let'
Read more about constants under 'Differences Between Variables and Constants' in the book's section 'Variables and Mutability':
'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
@ -78,48 +73,44 @@ 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?"""
`call_me`
`main` """
[[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`."""
Rust signature `call_me` `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."""
, *declaration* """
[[exercises]]
name = "functions4"
path = "exercises/functions/functions4.rs"
mode = "compile"
hint = """
The error message points to line 14 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!"""
15 `->`
`is_even` """
[[exercises]]
name = "functions5"
path = "exercises/functions/functions5.rs"
mode = "compile"
hint = """
This is a really common error that can be fixed by removing one character.
It happens because Rust distinguishes between expressions and statements: expressions return
a value based on its operand, and statements simply return a () type which behaves just like `void` in C/C++ language.
We want to return a value of `i32` type from the `square` function, but it is returning a `()` type...
They are not the same. There are two solutions:
1. Add a `return` ahead of `num * num;`
2. remove `;`, make it to be `num * num`"""
Rust operand,
`()` C/C++ `void`
`square` `i32` `()` ...
1. `num * num;` `return`
2. `;` `num * num`"""
# IF
@ -128,23 +119,23 @@ 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."""
!
:
- 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 = """
For that first compiler error, it's important in Rust that each conditional
block return the same type! To get the tests passing, you will need a couple
conditions checking different input values."""
Rust
conditional block
"""
# TEST 1

Loading…
Cancel
Save