Merge remote-tracking branch 'upstream/main'

pull/130/head
lijinpeng 3 years ago
commit ba3925dfb6

BIN
.DS_Store vendored

Binary file not shown.

@ -19,6 +19,17 @@
总之在写作过程中我们始终铭记初心:为中国用户打造一本**全面的、深入的、持续更新的**Rust教程。 新手用来入门,老手用来提高,高手用来提升生产力。
### Contributors
非常感谢本教程的所有贡献者们,你们的任何一处修改都将使教程内容变得更加优秀!
尤其感谢[@1132719438](https://github.com/1132719438)、[@codemystery](https://github.com/codemystery)和[@mg-chao](https://github.com/mg-chao)花费大量时间贡献了多处fix甚至是高质量的内容优化非常感动再次感谢
### 开源说明
Rust语言圣经是**完全开源**的电子书, 每个章节都至少用时4-6个小时才能初步完稿牺牲了大量休闲娱乐、陪伴家人的时间还没有任何钱赚**如果大家觉得这本书作者真的用心了,希望你能帮我们点一个`star`**,感激不尽:)
在开源版权上,我们选择了[No License](https://www.google.com.hk/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&ved=2ahUKEwigkv-KtMT0AhXFdXAKHdI4BCcQFnoECAQQAw&url=https%3A%2F%2Fchoosealicense.com%2Fno-permission%2F&usg=AOvVaw3M2Q4IbdhnpJ2K71TF7SPB)这意味着读者可以随意的fork和阅读但是**不能私下修改后再包装分发**,如果有这方面的需求,请联系我们,我们不会收钱,只是希望知道谁通过什么方式分发了这本书的部分内容,望理解.
### 感谢
本书在内容上部分借鉴了以下书籍,特此感谢:
- [Rust Book](https://doc.rust-lang.org/book)
@ -27,11 +38,6 @@
- [Async Book](https://rust-lang.github.io/async-book/01_getting_started/01_chapter.html)
详细清单参见[这里](https://github.com/sunface/rust-course/blob/main/writing-material/books.md)
### 开源说明
Rust语言圣经是**完全开源**的电子书, 每个章节都至少用时4-6个小时才能初步完稿牺牲了大量休闲娱乐、陪伴家人的时间还没有任何钱赚**如果大家觉得这本书作者真的用心了,希望你能帮我们点一个`star`**,感激不尽:)
在开源版权上,我们选择了[No License](https://www.google.com.hk/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&ved=2ahUKEwigkv-KtMT0AhXFdXAKHdI4BCcQFnoECAQQAw&url=https%3A%2F%2Fchoosealicense.com%2Fno-permission%2F&usg=AOvVaw3M2Q4IbdhnpJ2K71TF7SPB)这意味着读者可以随意的fork和阅读但是**不能私下修改后再包装分发**,如果有这方面的需求,请联系我们,我们不会收钱,只是希望知道谁通过什么方式分发了这本书的部分内容,望理解.
### Rust社区
与国外的Rust发展如火如荼相比国内的近况不是特别理想。导致目前这种状况的原因我个人认为有以下几点原因
@ -41,3 +47,4 @@ Rust语言圣经是**完全开源**的电子书, 每个章节都至少用时4-6
为此,我整了一本书和一个社区,欢迎大家的加入:
- QQ群1009730433

@ -67,16 +67,17 @@
- [智能指针 doing](advance/smart-pointer/intro.md)
- [Box<T>堆对象分配](advance/smart-pointer/box.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)
- [Drop释放资源](advance/smart-pointer/drop.md)
- [Cell与RefCell todo](advance/smart-pointer/cell.md)
- [Rc与Arc todo](advance/smart-pointer/rc-refcell.md)
- [自引用与内存泄漏 todo](advance/smart-pointer/self-referrence.md)
- [全局变量 todo](advance/global-variable.md)
- [多线程 todo](advance/multi-threads/intro.md)
- [线程管理(todo)](advance/multi-threads/thread.md)
- [消息传递(todo)](advance/multi-threads/message-passing.md)
- [数据共享Arc、Mutex、Rwlock(todo)](advance/multi-threads/ref-counter-lock.md)
- [数据竞争(todo)](advance/multi-threads/races.md)
- [Send、Sync(todo)](advance/multi-threads/send-sync.md)
- [线程管理 todo](advance/multi-threads/thread.md)
- [消息传递 todo](advance/multi-threads/message-passing.md)
- [数据共享Mutex、Rwlock todo](advance/multi-threads/ref-counter-lock.md)
- [数据竞争 todo](advance/multi-threads/races.md)
- [Send、Sync todo](advance/multi-threads/send-sync.md)
## 专题内容,每个专题都配套一个小型项目进行实践
- [Rust最佳实践 doing](practice/intro.md)
@ -94,6 +95,18 @@
- [不太勤快的迭代器](pitfalls/lazy-iterators.md)
- [奇怪的序列x..y](pitfalls/weird-ranges.md)
- [对抗编译检查 doing](fight-with-compiler/intro.md)
- [幽灵数据(todo)](fight-with-compiler/phantom-data.md)
- [生命周期)](fight-with-compiler/lifetime/intro.md)
- [生命周期过大-01](fight-with-compiler/lifetime/too-long1.md)
- [生命周期过大-02](fight-with-compiler/lifetime/too-long2.md)
- [循环中的生命周期](fight-with-compiler/lifetime/loop.md)
- [闭包碰到特征对象-01](fight-with-compiler/lifetime/closure-with-static.md)
- [重复借用](fight-with-compiler/borrowing/intro.md)
- [同时在函数内外使用引用](fight-with-compiler/borrowing/ref-exist-in-out-fn.md)
- [类型未限制(todo)](fight-with-compiler/unconstrained.md)
- [错误处理 todo](errors/intro.md)
- [简化错误处理](errors/simplify.md)
- [自定义错误](errors/user-define.md)
@ -159,16 +172,6 @@
- [FFI外部语言用](unsafe/ffi.md)
- [那些会导致UB的代码](unsafe/ub.md)
- [对抗编译检查 doing](fight-with-compiler/intro.md)
- [幽灵数据(todo)](fight-with-compiler/phantom-data.md)
- [生命周期)](fight-with-compiler/lifetime/intro.md)
- [生命周期过大-01](fight-with-compiler/lifetime/too-long1.md)
- [生命周期过大-02](fight-with-compiler/lifetime/too-long2.md)
- [循环中的生命周期](fight-with-compiler/lifetime/loop.md)
- [重复借用](fight-with-compiler/borrowing/intro.md)
- [同时在函数内外使用引用](fight-with-compiler/borrowing/ref-exist-in-out-fn.md)
- [类型未限制(todo)](fight-with-compiler/unconstrained.md)
- [宏编程 todo](macro/intro.md)
- [过程宏(todo)](macro/procedure-macro.md)

@ -140,7 +140,7 @@ fn f<'a, T>(x: *const T) -> &'a T {
我们在实际应用中,要尽量避免这种无界生命周期。最简单的避免无界生命周期的方式就是在函数声明中运用生命周期消除规则。**若一个输出生命周期被消除了,那么必定因为有一个输入生命周期与之对应**。
## 生命周期约束
## 生命周期约束HRTB
生命周期约束跟特征约束类似,都是通过形如`'a: 'b`的语法,来说明两个生命周期的长短关系。
#### 'a: 'b

@ -1 +1,4 @@
# Send、Sync(todo)
https://www.reddit.com/r/learnrust/comments/rufs0n/can_a_reference_to_a_type_mytype_implement/

@ -1,5 +1,5 @@
# Deref解引用
智能指针的名称来源,主要就在于它实现了`Deref`和`Drop`特征,这两个特征可以智能地帮助我们节省使用上的负担:
何为智能指针?能不让你写出&&&&&&s形式的解引用我认为就是智能: ) 智能指针的名称来源,主要就在于它实现了`Deref`和`Drop`特征,这两个特征可以智能地帮助我们节省使用上的负担:
- `Deref`可以让智能指针像引用那样工作,这样你就就可以写出同时支持智能指针和引用的代码, 例如`&T`
- `Drop`允许你指定智能指针超出作用域后自动执行的代码,例如做一些数据清除等收尾工作

@ -0,0 +1,183 @@
# Drop释放资源
在Rust中我们之所以可以一拳打跑GC的同时一脚踢翻手动资源回收主要就归功于`Drop`特征,同时它也是智能指针的必备特征之一。
## 学习目标
如何自动和手动释放资源及执行指定的收尾工作
## Rust中的资源回收
在一些无GC语言中程序员在一个变量无需再被使用时需要手动释放它占用的内存资源如果忘记了那么就会发生内存泄漏最终臭名昭著的`OOM`问题可能就会发生。
而在Rust中你可以指定在一个变量超出作用域时执行一段特定的代码最终编译器将帮你自动插入这段收尾代码。这样就无需在每一个使用该变量的地方都写一段代码来进行收尾工作和资源释放。不仅让人感叹Rust的大腿真粗
没错,指定这样一段收尾工作靠的就是咱这章的主角 - `Drop`特征。
## 一个不那么简单的Drop例子
```rust
struct HasDrop1;
struct HasDrop2;
impl Drop for HasDrop1 {
fn drop(&mut self) {
println!("Dropping HasDrop1!");
}
}
impl Drop for HasDrop2 {
fn drop(&mut self) {
println!("Dropping HasDrop2!");
}
}
struct HasTwoDrops {
one: HasDrop1,
two: HasDrop2,
}
impl Drop for HasTwoDrops {
fn drop(&mut self) {
println!("Dropping HasTwoDrops!");
}
}
struct Foo;
impl Drop for Foo {
fn drop(&mut self) {
println!("Dropping Foo!")
}
}
fn main() {
let _x = HasTwoDrops { two: HasDrop2 ,one: HasDrop1,};
let _foo = Foo;
println!("Running!");
}
```
上面代码虽然长,但是目的其实很单纯,就是为了观察不同情况下的`Drop`,变量级别的、结构体内部字段的, 有几点值得注意:
- `Drop`特征中的`drop`方法借用了目标的可变引用,而不是拿走了所有权,这里先设置一个悬念,后边会讲
- 结构体中每个字段都有自己的`Drop`
来看看输出:
```console
Running!
Dropping Foo!
Dropping HasTwoDrops!
Dropping HasDrop1!
Dropping HasDrop2!
```
嗯,结果符合预期,每个资源都成功的执行了收尾工作,虽然`println!`这种收尾工作毫无意义 = , =
#### Drop的顺序
观察以上输出,我们可以得出以下关于`Drop`顺序的结论
- **变量级别,按照逆序的方式**,`_x`在`_foo`之前创建,因此`_x`在`_foo`之后被drop
- **结构体内部,按照顺序的方式**, 结构体`_x`中的字段按照定义中的顺序依次`drop`
#### 没有实现Drop的结构体
实际上,就算你不为`_x`结构体实现`Drop`特征,它内部的两个字段依然会调用`drop`,移除以下代码,并观察输出:
```rust
impl Drop for HasTwoDrops {
fn drop(&mut self) {
println!("Dropping HasTwoDrops!");
}
}
```
原因在于Rust自动为几乎所有类型都实现了`Drop`特征,因此就算你不手动为结构体实现`Drop`,它依然会调用默认实现的`drop`函数,同时再调用每个字段的`drop`方法,最终打印出:
```cnosole
Dropping HasDrop1!
Dropping HasDrop2!
```
## 手动回收
当使用智能指针来管理锁的时候,你可能希望提前释放这个锁,然后让其它代码能及时获得锁,此时就需要提前去手动`drop`。
但是在之前我们提到一个悬念,就是`Drop::drop`只是借用了目标值的可变引用就算你提前调用了该方法但是后面的代码依然可以使用目标值这就会访问一个并不存在的值非常不安全好在Rust会阻止你
```rust
#[derive(Debug)]
struct Foo;
impl Drop for Foo {
fn drop(&mut self) {
println!("Dropping Foo!")
}
}
fn main() {
let foo = Foo;
foo.drop();
println!("Running!:{:?}", foo);
}
```
报错如下:
```console
error[E0040]: explicit use of destructor method
--> src/main.rs:37:9
|
37 | foo.drop();
| ----^^^^--
| | |
| | explicit destructor calls not allowed
| help: consider using `drop` function: `drop(foo)`
```
如上所示,编译器直接阻止了我们调用`Drop`特征的`drop`方法原因是该方法是析构函数这是一个用来清理实例的通用编程概念对于Rust而言不允许显式的调用析构函数。好在在报错的同时编译器还给出了一个提示使用`drop`函数。
针对编译器提示的`drop`函数,我们可以大胆推测下:它能够拿走目标值的所有权。现在来看看这个猜测正确与否,以下是`std::mem::drop`函数的签名:
```rust
pub fn drop<T>(_x: T)
```
如上所示,`drop`函数确实拿走了目标值的所有权,来验证下:
```rust
fn main() {
let foo = Foo;
drop(foo);
// 以下代码会报错:借用了所有权被转移的值
// println!("Running!:{:?}", foo);
}
```
Bingo完美拿走了所有权而且这种实现保证了后续的使用必定会导致编译错误因此非常安全
细心的同学可能已经注意到,这里直接调用了`drop`函数,并没有引入任何模块信息,原因是该函数在[`std::prelude`](../../appendix/prelude.md)里。
## Drop使用场景
对于Drop而言主要有两个功能
- 回收内存资源
- 执行一些收尾工作
对于第二点,在之前我们已经详细介绍过,因此这里主要对第一点进行下简单说明。
在绝大多数情况下,我们都无需手动去`drop`以回收内存资源因为Rust会自动帮我们完成这些工作它甚至会对复杂类型的每个字段都单独的调用`drop`进行回收但是确实有极少数情况需要你自己来回收资源的例如文件描述符、网络socket等当这些超出作用域不再使用时就需要进行关闭以释放相关的资源在这些情况下就需要使用者自己来解决`Drop`的问题。
## 互斥的Copy和Drop
我们无法为一个类型同时实现`Copy`和`Drop`特征。因为实现了`Copy`的特征会被编译器隐式的复制,因此非常难以预测析构函数执行的时间和频率。因此这些实现了`Copy`的类型无法拥有析构函数。
```rust
#[derive(Copy)]
struct Foo;
impl Drop for Foo {
fn drop(&mut self) {
println!("Dropping Foo!")
}
}
```
以下代码报错如下:
```console
error[E0184]: the trait `Copy` may not be implemented for this type; the type has a destructor
--> src/main.rs:24:10
|
24 | #[derive(Copy)]
| ^^^^ Copy not allowed on types with destructors
```
## 总结
`Drop`可以用于许多方面,来使得资源清理及收尾工作变得方便和安全,甚至可以用其创建我们自己的内存分配器!通过`Drop`特征和 Rust 所有权系统你无需担心之后的代码清理Rust 会自动考虑这些问题。
我们也无需担心意外的清理掉仍在使用的值,这会造成编译器错误:所有权系统确保引用总是有效的,也会确保`drop`只会在值不再被使用时被调用一次。

@ -0,0 +1,163 @@
# 当闭包碰到特征对象1
特征对象是一个好东西闭包也是一个好东西但是如果两者你都想要时可能就会火星撞地球boom! 至于这两者为何会勾搭到一起?考虑一个常用场景:使用闭包作为回调函数.
## 学习目标
如何使用闭包作为特征对象,并解决以下错误:`the parameter type `impl Fn(&str) -> Res` may not live long enough`
## 报错的代码
在下面代码中,我们通过闭包实现了一个简单的回调函数(错误代码已经标注)
```rust
pub struct Res<'a> {
value: &'a str,
}
impl<'a> Res<'a> {
pub fn new(value: &str) -> Res {
Res { value }
}
}
pub struct Container<'a> {
name: &'a str,
callback: Option<Box<dyn Fn(&str) -> Res>>,
}
impl<'a> Container<'a> {
pub fn new(name: &str) -> Container {
Container {
name,
callback: None,
}
}
pub fn set(&mut self, cb: impl Fn(&str) -> Res) {
self.callback = Some(Box::new(cb));
}
}
fn main() {
let mut inl = Container::new("Inline");
inl.set(|val| {
println!("Inline: {}", val);
Res::new("inline")
});
if let Some(cb) = inl.callback {
cb("hello, world");
}
}
```
从第一感觉来说,报错属实不应该,因为我们连引用都没有用,生命周期都不涉及,怎么就报错了?在继续深入之前,先来观察下该闭包是如何被使用的:
```rust
callback: Option<Box<dyn Fn(&str) -> Res>>,
```
众所周知,闭包跟哈姆雷特一样,每一个都有[自己的类型](../../advance/functional-programing/closure.md#闭包作为函数返回值),因此我们无法通过类型标注的方式来声明一个闭包,那么只有一个办法,就是使用特征对象,因此上面代码中,通过`Box<dyn Trait>`的方式把闭包特征封装成一个特征对象。
## 深入挖掘报错原因
事出诡异必有妖,那接下来我们一起去会会这只妖。
#### 特征对象的生命周期
首先编译器报错提示我们闭包活得不够久,那可以大胆推测,正因为使用了闭包作为特征对象,所以才活得不够久。因此首先需要调查下特征对象的生命周期。
首先给出结论:**特征对象隐式的具有`'static`生命周期**。
其实在Rust中`'static`生命周期很常见,例如一个没有引用字段的结构体它其实也是`'static`。当`'static`用于一个类型时,该类型不能包含任何非`'static`引用字段,例如以下结构体:
```rust
struct Foo<'a> {
x : &'a [u8]
};
```
除非`x`字段借用了`'static`的引用,否则`'a`肯定比`'static`要小,那么该结构体实例的生命周期肯定不是`'static`: `'a: 'static`的限制不会被满足([HRTB](../../advance/lifetime/advance.md#生命周期约束HRTB))。
对于特征对象来说,它没有包含非`'static`的引用,因此它隐式的具有`'static`生命周期, `Box<dyn Trait>`就跟`Box<dyn Trait + 'static>`是等价的。
#### 'static闭包的限制
其实以上代码的错误很好解决,甚至编译器也提示了我们:
```console
help: consider adding an explicit lifetime bound...: `impl Fn(&str) -> Res + 'static`
```
但是解决问题不是本文的目标,我们还是要继续深挖一下,如果闭包使用了`'static`会造成什么问题。
##### 1. 无本地变量被捕获
```rust
inl.set(|val| {
println!("Inline: {}", val);
Res::new("inline")
});
```
以上代码只使用了闭包中传入的参数,并没有本地变量被捕获,因此`'static`闭包一切OK。
##### 2. 有本地变量被捕获
```rust
let local = "hello".to_string();
// 编译错误: 闭包不是'static!
inl.set(|val| {
println!("Inline: {}", val);
println!("{}", local);
Res::new("inline")
});
```
这里我们在闭包中捕获了本地环境变量`local`,因为`local`不是`'static`,那么闭包也不再是`'static`。
##### 3. 将本地变量move进闭包
```rust
let local = "hello".to_string();
inl.set(move |val| {
println!("Inline: {}", val);
println!("{}", local);
Res::new("inline")
});
// 编译错误: local已经被移动到闭包中这里无法再被借用
// println!("{}", local);
```
如上所示,你也可以选择将本地变量的所有权`move`进闭包中,此时闭包再次具有`'statci`生命周期
##### 4. 非要捕获本地变量的引用?
对于第2种情况如果非要这么干那`'static`肯定是没办法了,我们只能给予闭包一个新的生命周期:
```rust
pub struct Container<'a, 'b> {
name: &'a str,
callback: Option<Box<dyn Fn(&str) -> Res + 'b>>,
}
impl<'a, 'b> Container<'a, 'b> {
pub fn new(name: &str) -> Container {
Container {
name,
callback: None,
}
}
pub fn set(&mut self, cb: impl Fn(&str) -> Res + 'b) {
self.callback = Some(Box::new(cb));
}
}
```
肉眼可见,代码复杂度哐哐哐提升,不得不说`'static`真香!
友情提示:由此修改引发的一系列错误,需要你自行修复: ) (再次友情小提示,可以考虑把`main`中的`local`变量声明位置挪到`inl`声明位置之前)
## 姗姗来迟的正确代码
其实,大家应该都知道该如何修改了,不过出于严谨,我们还是继续给出完整的正确代码:
```rust
pub fn set(&mut self, cb: impl Fn(&str) -> Res + 'static) {
```
可能大家觉得我重新定义了`完整`两个字,其实是我不想水篇幅:)
## 总结
闭包和特征对象的相爱相杀主要原因就在于特征对象默认具备`'static`的生命周期,同时我们还对什么样的类型具备`'static`进行了简单的分析。
同时,如果一个闭包拥有`'static`生命周期,那闭包无法通过引用的方式来捕获本地环境中的变量。如果你想要非要捕获,只能使用非`'static`。

@ -1,10 +1,10 @@
# Move Semantics
# 移动语义(Move Semantics
These exercises are adapted from [pnkfelix](https://github.com/pnkfelix)'s [Rust Tutorial](https://pnkfelix.github.io/rust-examples-icfp2014/) -- Thank you Felix!!!
这些练习改编自 [pnkfelix](https://github.com/pnkfelix) 的 [Rust Tutorial](https://pnkfelix.github.io/rust-examples-icfp2014/) -- 谢谢 Felix !!!
## Further information
## 更多信息
For this section, the book links are especially important.
以下书籍中的内容对于当前的学习尤其重要。
- [Ownership](https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html)
- [Reference and borrowing](https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html)

@ -1,5 +1,5 @@
// move_semantics1.rs
// Make me compile! Execute `rustlings hint move_semantics1` for hints :)
// 让我能够编译!执行 `rustex hint move_semantics1` 获取提示 :)
// I AM NOT DONE
@ -8,7 +8,7 @@ fn main() {
let vec1 = fill_vec(vec0);
println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1);
println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1);// 译:"{} 长度为 {} 内容是 `{:?}`"
vec1.push(88);

@ -1,6 +1,6 @@
// move_semantics2.rs
// Make me compile without changing line 13!
// Execute `rustlings hint move_semantics2` for hints :)
// 在不更改第 13 行的要求下通过编译!
// 执行 `rustex hint move_semantics2` 获取提示 :)
// I AM NOT DONE
@ -9,7 +9,7 @@ fn main() {
let mut vec1 = fill_vec(vec0);
// Do not change the following line!
// 不要更改下面那行!
println!("{} has length {} content `{:?}`", "vec0", vec0.len(), vec0);
vec1.push(88);

@ -1,7 +1,7 @@
// move_semantics3.rs
// Make me compile without adding new lines-- just changing existing lines!
// (no lines with multiple semicolons necessary!)
// Execute `rustlings hint move_semantics3` for hints :)
// 在不添加新行仅改变已有行的要求下通过编译!
// (也不允许有多个分号的行!)
// 执行 `rustex hint move_semantics3` 获取提示 :)
// I AM NOT DONE

@ -1,8 +1,7 @@
// move_semantics4.rs
// Refactor this code so that instead of having `vec0` and creating the vector
// in `fn main`, we create it within `fn fill_vec` and transfer the
// freshly created vector from fill_vec to its caller.
// Execute `rustlings hint move_semantics4` for hints!
// 重构这段代码,做到删除 `vec0` ,并在 `fn fill_vec` 而非 `fn main` 中创建 vector
// 然后将新创建的 vector 从 `fill_vec` 转移到其调用者。
// 执行 `rustex hint move_semantics4` 获取提示 :)
// I AM NOT DONE
@ -18,7 +17,7 @@ fn main() {
println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1);
}
// `fill_vec()` no longer takes `vec: Vec<i32>` as argument
// `fill_vec()` 不再获取 `vec: Vec<i32>` 参数
fn fill_vec() -> Vec<i32> {
let mut vec = vec;

@ -1,7 +1,6 @@
// move_semantics5.rs
// Make me compile only by reordering the lines in `main()`, but without
// adding, changing or removing any of them.
// Execute `rustlings hint move_semantics5` for hints :)
// 只通过重新排列 `main()` 中的已有行来完成编译,并且不能增加、更改或删除任何行
// 执行 `rustex hint move_semantics5` 获取提示 :)
// I AM NOT DONE

@ -152,64 +152,57 @@ 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."""
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 = """
So `vec0` is being *moved* into the function `fill_vec` when we call it on
line 10, which means it gets dropped at the end of `fill_vec`, which means we
can't use `vec0` again on line 13 (or anywhere else in `main` after the
`fill_vec` call for that matter). We could fix this in a few ways, try them
all!
1. Make another, separate version of the data that's in `vec0` and pass that
to `fill_vec` instead.
2. Make `fill_vec` borrow its argument instead of taking ownership of it,
and then copy the data within the function in order to return an owned
`Vec<i32>`
3. Make `fill_vec` *mutably* borrow its argument (which will need to be
mutable), modify it directly, then not return anything. Then you can get rid
of `vec1` entirely -- note that this will change what gets printed by the
first `println!`"""
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 = """
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 :)"""
`fn fill_vec` `let mut vec = vec;`
`mut` 使 :)"""
[[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`"""
:)
- 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 = """
Carefully reason about the range in which each mutable reference is in
vogue. 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':
使
x
'References and Borrowing' 'Mutable References'
https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-references.
"""

Loading…
Cancel
Save