add 陷阱6

pull/107/head
sunface 3 years ago
parent e18a7484a2
commit 8c9ceb5813

@ -62,7 +62,6 @@
- [包crate](advance/crate-module/crate.md)
- [模块Module](advance/crate-module/module.md)
- [使用use引入模块及受限可见性](advance/crate-module/use.md)
- [工作空间workspace](advance/crate-module/workspace.md)
- [深入类型之newtype和Sized](advance/custom-type.md)
- [格式化输出](advance/formatted-output.md)
- [智能指针 todo](advance/smart-pointer/intro.md)
@ -87,7 +86,8 @@
- [闭包中奇怪的生命周期](pitfalls/closure-with-lifetime.md)
- [可变变量不可变?](pitfalls/the-disabled-mutability.md)
- [可变借用失败引发的深入思考](pitfalls/multiple-mutable-references.md)
- [不太勤快的迭代器](pitfalls/lazy-iterators.md)
- [Rust最佳实践 todo](practice/intro.md)
- [一些写代码的技巧 todo](practice/coding-tips.md)
- [最佳实践 todo](practice/best-pratice.md)

@ -68,7 +68,7 @@ fn main() {
## 避免同名引用
根据上一章节的内容,我们只要保证同一个模块中不存在同名项就行,模块之间、包之间的同名,谁管的着谁啊,话虽如此,一起看看,如果遇到同名的情况该如何处理.
#### 使用模块::函数
#### 模块::函数
```rust
use std::fmt;
use std::io;

@ -228,6 +228,8 @@ Rust需要明确的知道一个特定类型的值占据了多少内存空间
我们之前已经见过,使用`Box`将一个没有固定大小的特征变成一个有固定大小的特征对象,那能否故技重施,将`str`封装成一个固定大小类型?留个悬念先,我们来看看`Sized`特征。
> Rust中最常见的`DST`类型: `str`, `[T]`, `dyn Trait`
#### Sized特征
既然动态类型的问题这么大那么在使用泛型时Rust如何保证我们的泛型参数是固定大小的类型呢例如以下泛型函数
```rust

@ -395,6 +395,7 @@ println!("{}", val);
```
## 迭代器的性能
@todo
## 学习其它方法
迭代器用的好不好,就在于你是否掌握了它的常用方法,且能活学活用,因此多多看看[标准库](https://doc.rust-lang.org/std/iter/trait.Iterator.html)是有好处的,只有知道有什么方法,在需要的时候你才能知道该用什么,就和算法学习一样。

@ -36,4 +36,7 @@ impl Factory{
}
}
}
```
```
## 从函数中返回全局变量
https://www.reddit.com/r/learnrust/comments/rqn74g/cant_a_function_return_a_reference_to_some_global/

@ -1 +1,3 @@
# Rc、Arc、Mutex、Rwlock(todo)
https://oribenshir.github.io/afternoon_rusting//blog/deref-smart-pointer

@ -1 +1,9 @@
# 文档注释 todo
# 注释及文档
Rust有一点特别
## 文档注释查看
https://doc.rust-lang.org/book/ch14-02-publishing-to-crates-io.html#making-useful-documentation-comments
## 文档注释中写测试

@ -0,0 +1,112 @@
# 不太勤快的迭代器
迭代器在Rust中是一个非常耀眼的存在它光鲜亮丽它让Rust大道至简它备受用户的喜爱。可是它也是懒惰的不信一起来看看。
## for循环 vs 迭代器
在迭代器学习中,我们提到过迭代器在功能上可以替代循环,性能上略微优于循环(避免边界检查),安全性上优于循环因此在Rust中迭代器往往都是更优的选择前提是迭代器得发挥作用。
在下面代码中,分别是使用`for`循环和迭代器去生成一个`HashMap`。
使用循环:
```rust
use std::collections::HashMap;
#[derive(Debug)]
struct Account {
id: u32,
}
fn main() {
let accounts = [Account { id: 1 }, Account { id: 2 }, Account { id: 3 }];
let mut resolvers = HashMap::new();
for a in accounts {
resolvers.entry(a.id).or_insert(Vec::new()).push(a);
}
println!("{:?}",resolvers);
}
```
使用迭代器:
```rust
let mut resolvers = HashMap::new();
accounts.into_iter().map(|a| {
resolvers
.entry(a.id)
.or_insert(Vec::new())
.push(a);
});
println!("{:?}",resolvers);
```
#### 预料之外的结果
两端代码乍一看(很多时候我们快速浏览代码的时候,不会去细看)都很正常, 运行下试试:
- `for`循环很正常,输出`{2: [Account { id: 2 }], 1: [Account { id: 1 }], 3: [Account { id: 3 }]}`
- 迭代器很。。。不正常,输出了一个`{}`, 黑人问号`? ?` **?**
在继续深挖之前,我们先来简单回顾下迭代器。
## 回顾下迭代器
在迭代器章节中,我们曾经提到过,迭代器的[适配器](../advance/functional-programing/iterator.md#消费者与适配器)分为两种:消费者适配器和迭代器适配器,前者用来将一个迭代器变为指定的集合类型,往往通过`collect`实现;后者用于生成一个新的迭代器,例如上例中的`map`。
还提到过非常重要的一点: **迭代器适配器都是懒惰的,只有配合消费者适配器使用时,才会进行求值**.
## 懒惰是根因
在我们之前的迭代器示例中,只有一个迭代器适配器`map`:
```rust
accounts.into_iter().map(|a| {
resolvers
.entry(a.id)
.or_insert(Vec::new())
.push(a);
});
```
首先, `accounts`被拿走所有权后转换成一个迭代器,其次该迭代器通过`map`方法生成一个新的迭代器,最后,在此过程中没有以类如`collect`的消费者适配器收尾。
因此在上述过程中,`map`完全是懒惰的,它没有做任何事情,它在等一个消费者适配器告诉它:赶紧起床,任务可以开始了,它才会开始行动。
自然,我们的插值计划也失败了。
> 事实上IDE和编译器都会对这种代码给出警告iterators are lazy and do nothing unless consumed
## 解决办法
原因非常清晰,如果读者还有疑惑,建议深度下上面给出的迭代器链接,我们这里就不再赘述。
下面列出三种合理的解决办法:
1. 不再使用迭代器适配器`map`,改成`for_each`:
```rust
let mut resolvers = HashMap::new();
accounts.into_iter().for_each(|a| {
resolvers
.entry(a.id)
.or_insert(Vec::new())
.push(a);
});
```
但是,相关的文档也友善的提示了我们,除非作为链式调用的收尾,否则更建议使用`for`循环来处理这种情况。哎,忙忙碌碌,又回到了原点,不禁让人感叹:天道有轮回。
2. 使用消费者适配器`collect`来收尾,将`map`产生的迭代器收集成一个集合类型:
```rust
let resolvers: HashMap<_, _> = accounts
.into_iter()
.map(|a| (a.id, a))
.collect();
```
嗯,还挺简洁,挺`rusty`.
3. 使用`fold`,语义表达更强:
```rust
let resolvers = account.into_iter().fold(HashMap::new(), |mut resolvers, a|{
resolvers.entry(a.id).or_insert(Vec::new).push(a);
resolvers
});
```
## 总结
在使用迭代器时,要清晰的认识到需要用到的方法是迭代型还是消费型适配器,如果一个调用链中没有以消费型适配器结尾,就需要打起精神了,也许,不远处就是一个陷阱在等你跳:)

@ -0,0 +1,2 @@
https://www.reddit.com/r/rust/comments/rqgwaz/why_is_my_simd_code_slower_than_the_naive_one/
Loading…
Cancel
Save