Skip to content

Commit fdcd96d

Browse files
committed
👀 Reformat: Fix EditorConfig-related Problems
i.e. ``` end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true ```
1 parent 43c0cb5 commit fdcd96d

38 files changed

Lines changed: 321 additions & 321 deletions

action/db/readme.md

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ postgres="*"
2525

2626
当然我们还是进行最简单的操作,直接粘贴复制,[代码来源](https://github.com/sfackler/rust-postgres#overview)
2727

28-
``` rust
28+
``` rust
2929

3030
extern crate postgres;
3131

@@ -108,10 +108,10 @@ pub fn insert_info(conn : &Connection,title : &str, body: &str){
108108
let stmt = match conn.prepare("insert into blog (title, body) values ($1, $2)") {
109109
Ok(stmt) => stmt,
110110
Err(e) => {
111-
println!("Preparing query failed: {:?}", e);
111+
println!("Preparing query failed: {:?}", e);
112112
return;
113-
}
114-
};
113+
}
114+
};
115115
stmt.execute(&[&title, &body]).expect("Inserting blogposts failed");
116116
}
117117

@@ -126,9 +126,9 @@ pub fn query<T>(conn: &Connection,query: &str) ->PgResult<T>
126126
//rows.iter().next().unwrap()
127127
row.get_opt(2).unwrap()
128128

129-
}
129+
}
130130

131-
pub fn query_all(conn: &Connection,query: &str){
131+
pub fn query_all(conn: &Connection,query: &str){
132132
println!("Executing query: {}", query);
133133
for row in &conn.query(query,&[]).unwrap(){
134134
let person = Person{
@@ -139,7 +139,7 @@ pub fn query_all(conn: &Connection,query: &str){
139139
println!("Found person {}", person.name);
140140
}
141141

142-
}
142+
}
143143

144144
```
145145
然后在main.rs 中调用相应的函数代码如下
@@ -150,7 +150,7 @@ pub fn query_all(conn: &Connection,query: &str){
150150

151151
``` rust
152152
extern crate postgres;
153-
extern crate db;
153+
extern crate db;
154154

155155
use postgres::{Connection, SslMode};
156156

@@ -162,19 +162,19 @@ struct Blog {
162162
}
163163

164164
fn main() {
165-
let conn:Connection=connect();
166-
165+
let conn:Connection=connect();
166+
167167
let blog = Blog{
168168
title: String::from("title"),
169169
body: String::from("body"),
170-
};
170+
};
171171
let title = blog.title.to_string();
172172
let body = blog.body.to_string();
173-
insert_info(&conn,&title,&body);
173+
insert_info(&conn,&title,&body);
174174

175175
for row in query::<String>(&conn,"select * from blog"){
176176
println!("{:?}",row);
177-
}
177+
}
178178
let sql = "select * from person";
179179
query_all(&conn,&sql);
180180
}
@@ -192,14 +192,14 @@ fn main() {
192192
说返回的是一个可迭代的数据,那也就是说,我可以使用for循环,将数据打印,
193193
但是发现怎么也不能实现:
194194

195-
``` rust
195+
``` rust
196196

197-
pub fn query_all(conn: &Connection,query: &str){
197+
pub fn query_all(conn: &Connection,query: &str){
198198
println!("Executing query: {}", query);
199-
for row in &conn.query(query,&[]).unwrap(){
199+
for row in &conn.query(query,&[]).unwrap(){
200200
println!("Found person {:?}", row.get_opt(1));
201201
}
202-
}
202+
}
203203

204204
```
205205
报错如下:

coding-style/style.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ let diameter = 7;
149149
/// # 参数
150150
///
151151
/// * `argc` 和 `argv` - 参数向量。在 Unix 系统上,该信息被`os::args`使用。
152-
///
152+
///
153153
/// * `main` - 运行在 M:N 调度器池内的初始过程。
154154
/// 一旦这个过程退出,调度池将开始关闭。
155155
/// 整个池(和这个函数)将只有在所有子线程完成执行后。

concurrency-parallel-threads/message-passing.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ receive 1
3737
3. 通道的发送者和接收者支持N:1,1:N, N:N模式吗?
3838
4. 通道能发送任何数据吗?
3939
5. 发送后的数据,在线程中继续使用没有问题吗?
40-
40+
4141
让我们带着这些问题和思考进入下一个小节,那里有相关的答案。
4242

4343
### 消息类型
@@ -81,23 +81,23 @@ note: `alloc::rc::Rc<Student>` cannot be sent between threads safely
8181
```
8282
看来并不是所有类型的消息都可以通过通道发送,消息类型必须实现`marker trait Send`。Rust之所以这样强制要求,主要是为了解决并发安全的问题,再一次强调,**安全**是Rust考虑的重中之重。如果一个类型是`Send`,则表明它可以在线程间安全的转移所有权(`ownership`),当所有权从一个线程转移到另一个线程后,同一时间就只会存在一个线程能访问它,这样就避免了数据竞争,从而做到线程安全。`ownership`的强大又一次显示出来了。通过这种做法,在编译时即可要求所有的代码必须满足这一约定,这种方式方法值得借鉴,`trait`也是非常强大。
8383

84-
看起来问题得到了完美的解决,然而由于`Send`本身是一个不安全的`marker trait`,并没有实际的`API`,所以实现它很简单,但没有强制保障,就只能靠开发者自己约束,否则还是可能引发并发安全问题。对于这一点,也不必太过担心,因为Rust中已经存在的类,都已经实现了`Send``!Send`,我们只要使用就行。`Send`是一个默认应用到所有Rust已存在类的trait,所以我们用`!Send`显式标明该类没有实现`Send`。目前几乎所有的原始类型都是`Send`,例如前面例子中发送的`i32`。对于开发者而言,我们可能会更关心哪些是非`Send`,也就是实现了`!Send`,因为这会导致线程不安全。更全面的信息参见[`Send`官网API](https://doc.rust-lang.org/std/marker/trait.Send.html)
84+
看起来问题得到了完美的解决,然而由于`Send`本身是一个不安全的`marker trait`,并没有实际的`API`,所以实现它很简单,但没有强制保障,就只能靠开发者自己约束,否则还是可能引发并发安全问题。对于这一点,也不必太过担心,因为Rust中已经存在的类,都已经实现了`Send``!Send`,我们只要使用就行。`Send`是一个默认应用到所有Rust已存在类的trait,所以我们用`!Send`显式标明该类没有实现`Send`。目前几乎所有的原始类型都是`Send`,例如前面例子中发送的`i32`。对于开发者而言,我们可能会更关心哪些是非`Send`,也就是实现了`!Send`,因为这会导致线程不安全。更全面的信息参见[`Send`官网API](https://doc.rust-lang.org/std/marker/trait.Send.html)
8585

8686
对于不是`Send`的情况(`!Send`),大致分为两类:
8787

8888
1. 原始指针,包括`*mut T`和`*const T`,因为不同线程通过指针都可以访问数据,从而可能引发线程安全问题。
8989
2. `Rc`和`Weak`也不是,因为引用计数会被共享,但是并没有做并发控制。
9090

9191
虽然有这些`!Send`的情况,但是逃不过编译器的火眼金睛,只要你错误地使用了消息类型,编译器都会给出类似于上面的错误提示。我们要担心的不是这些,因为错误更容易出现在新创建的自定义类,有下面两点需要注意:
92-
92+
9393
1. 如果自定义类的所有字段都是`Send`,那么这个自定义类也是`Send`。
9494
反之,如果有一个字段是`!Send`,那么这个自定义类也是`!Send`。
9595
如果类的字段存在递归包含的情况,按照该原则以此类推来推论类是`Send`还是`!Send`。
96-
96+
9797
2. 在为一个自定义类实现`Send`或者`!Send`时,必须确保符合它的约定。
9898

9999
到此,消息类型的相关知识已经介绍完了,说了这么久,也该让大家自己练习一下了:请实现一个自定义类,该类包含一个Rc字段,让这个类变成可以在通道中发送的消息类型。
100-
100+
101101
### 异步通道(Channel)
102102
在粗略地尝试通道之后,是时候更深入一下了。Rust的标准库其实提供了两种类型的通道:异步通道和同步通道。上面的例子都是使用的异步通道,为此这一小节我们优先进一步介绍异步通道,后续再介绍同步通道。异步通道指的是:不管接收者是否正在接收消息,消息发送者在发送消息时都不会阻塞。为了验证这一点,我们尝试多增加一个线程来发送消息:
103103

@@ -145,11 +145,11 @@ receive 2
145145
1. 通道是可以同时支持多个发送者的,通过`clone`的方式来实现。
146146
这类似于`Rc`的共享机制,其实从`Channel`所在的库名`std::sync::mpsc`也可以知道这点,因为`mpsc`就是多生产者单消费者(Multiple Producers Single Consumer)的简写。
147147
可以有多个发送者,但只能有一个接收者,即支持的N:1模式。
148-
148+
149149
2. 异步通道具备消息缓存的功能,因为1和2是在没有接收之前就发了的,在此之后还能接收到这两个消息。
150-
150+
151151
那么通道到底能缓存多少消息?在理论上是无穷的,尝试一下便知:
152-
152+
153153
```rust
154154
use std::sync::mpsc;
155155
use std::thread;
@@ -173,7 +173,7 @@ fn main() {
173173
return;
174174
},
175175
}
176-
}
176+
}
177177
});
178178

179179
// 在主线程中接收子线程发送的消息并输出
@@ -184,7 +184,7 @@ fn main() {
184184
最后的结果就是耗费内存为止。
185185

186186
3. 消息发送和接收的顺序是一致的,满足先进先出原则。
187-
187+
188188
上面介绍的内容大多是关于发送者和通道的,下面开始考察一下接收端。通过上面的几个例子,细心一点的可能已经发现接收者的`recv`方法应该会阻塞当前线程,如果不阻塞,在多线程的情况下,发送的消息就不可能接收完全。所以没有发送者发送消息,那么接收者将会一直等待,这一点要谨记。在某些场景下,一直等待是符合实际需求的。但某些情况下并不需一直等待,那么就可以考虑释放通道,只要通道释放了,`recv`方法就会立即返回。
189189

190190
异步通道的具有良好的灵活性和扩展性,针对业务需要,可以灵活地应用于实际项目中,实在是必备良药!
@@ -229,7 +229,7 @@ after send
229229

230230
1. 同步通道是需要指定缓存的消息个数的,但需要注意的是,最小可以是0,表示没有缓存。
231231
2. 发送者是会被阻塞的。当通道的缓存队列不能再缓存消息时,发送者发送消息时,就会被阻塞。
232-
232+
233233
对照上面两点和运行结果来分析,由于主线程在接收消息前先睡眠了,从而子线程这个时候会被调度执行发送消息,由于通道能缓存的消息为0,而这个时候接收者还没有接收,所以`tx.send(1).unwrap()`就会阻塞子线程,直到主线程接收消息,即执行`println!("receive {}", rx.recv().unwrap());`。运行结果印证了这点,要是没阻塞,那么在`before send`之后就应该是`after send`了。
234234

235235
相比较而言,异步通道更没有责任感一些,因为消息发送者一股脑的只管发送,不管接收者是否能快速处理。这样就可能出现通道里面缓存大量的消息得不到处理,从而占用大量的内存,最终导致内存耗尽。而同步通道则能避免这种问题,把接受者的压力能传递到发送者,从而一直传递下去。

concurrency-parallel-threads/parallel.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@ fn main() {
1818

1919
colors.par_iter_mut().for_each(|color| {
2020
let c : f32 = if *color < 0.0 {
21-
0.0
21+
0.0
2222
} else if *color > 255.0 {
23-
255.0
24-
} else {
25-
*color
23+
255.0
24+
} else {
25+
*color
2626
};
2727
*color = c / 255.0;
28-
});
28+
});
2929
println!("transformed: {:?}", &colors);
3030
}
3131
```

concurrency-parallel-threads/share-memory.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ use std::sync::Arc;
6969
fn main() {
7070
let var : Arc<i32> = Arc::new(5);
7171
let share_var = var.clone();
72-
72+
7373
// 创建一个新线程
7474
let new_thread = thread::spawn(move|| {
7575
println!("share value in new thread: {}, address: {:p}", share_var, &*share_var);
@@ -105,7 +105,7 @@ pub fn new(data: T) -> Arc<T> {
105105

106106
1. 资源何时释放?
107107
2. 线程如何安全的并发修改和读取?
108-
108+
109109
由于上面两个问题的存在,这就是为什么我们不能直接用`Box`变量在线程中共享的原因,可以看出来,共享内存比消息传递机制似乎要复杂许多。Rust用了引用计数的方式来解决第一个问题,在标准库中提供了两个包裹类,除了上面一个用于多线程的`std::sync::Arc`之外,还有一个不能用于多线程的`std::rc::Rc`。在使用时,可以根据需要进行选择。如果你一不小心把`std::rc::Rc`用于多线程中,编译器会毫不客气地纠正你的。
110110

111111
关于上面的第二个问题,Rust语言及标准库提供了一系列的同步手段来解决。下面的章节我们将详细讲解这些方式和用法。

concurrency-parallel-threads/thread.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ fn main() {
9393
println!("I am a new thread.");
9494
}
9595
});
96-
96+
9797
// 不等待新创建的线程执行完成
9898
// new_thread.join().unwrap();
9999
}
@@ -113,7 +113,7 @@ fn main() {
113113
}
114114
})
115115
});
116-
116+
117117
// 等待新创建的线程执行完成
118118
new_thread.join().unwrap();
119119
println!("Child thread is finish!");

data-structure/binary_tree.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
1. 二叉树**左节点的值通常少于父亲节点的值**
77
2. 二叉树**右节点的值通常大于父亲节点的值**
88

9-
>二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒。二叉树的第i层至多有2^{i-1}个结点;深度为k的二叉树至多有2^k-1个结点;对任何一棵二叉树T,如果其终端结点数为n_0,度为2的结点数为n_2,则n_0=n_2+1。
9+
>二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒。二叉树的第i层至多有2^{i-1}个结点;深度为k的二叉树至多有2^k-1个结点;对任何一棵二叉树T,如果其终端结点数为n_0,度为2的结点数为n_2,则n_0=n_2+1。
1010
11-
>一棵深度为k,且有2^k-1个节点称之为满二叉树;深度为k,有n个节点的二叉树,当且仅当其每一个节点都与深度为k的满二叉树中,序号为1至n的节点对应时,称之为完全二叉树。
11+
>一棵深度为k,且有2^k-1个节点称之为满二叉树;深度为k,有n个节点的二叉树,当且仅当其每一个节点都与深度为k的满二叉树中,序号为1至n的节点对应时,称之为完全二叉树。
1212
1313
## 二叉树与树的区别
1414
二叉树不是树的一种特殊情形,尽管其与树有许多相似之处,但树和二叉树有两个主要差别:
@@ -132,4 +132,4 @@ fn main() {
132132
## 练习
133133
基于以上代码,修改成二叉堆的形式。
134134

135-
>二叉堆是一种特殊的堆,二叉堆是完全二元树(二叉树)或者是近似完全二元树(二叉树)。二叉堆有两种:最大堆和最小堆。最大堆:父结点的键值总是大于或等于任何一个子节点的键值;最小堆:父结点的键值总是小于或等于任何一个子节点的键值。
135+
>二叉堆是一种特殊的堆,二叉堆是完全二元树(二叉树)或者是近似完全二元树(二叉树)。二叉堆有两种:最大堆和最小堆。最大堆:父结点的键值总是大于或等于任何一个子节点的键值;最小堆:父结点的键值总是小于或等于任何一个子节点的键值。

data-structure/graph.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ impl Edge {
5353
edge: true,
5454
}
5555
}
56-
}
56+
}
5757

5858
impl Graphadj {
5959
fn new(nums:usize) -> Graphadj {
@@ -85,11 +85,11 @@ fn main() {
8585
println!("{:?}", g);
8686
}
8787
```
88-
88+
8989
## 邻接表表示法
9090

9191
邻接表是图的一种最主要存储结构,用来描述图上的每一个点。
92-
92+
9393
>**实现方式:**对图的每个顶点建立一个容器(n个顶点建立n个容器),第i个容器中的结点包含顶点Vi的所有邻接顶点。实际上我们常用的邻接矩阵就是一种未离散化每个点的边集的邻接表。
9494
9595
* 在有向图中,描述每个点向别的节点连的边(点 a->点 b 这种情况)。
@@ -98,4 +98,4 @@ fn main() {
9898
与邻接表相对应的存图方式叫做边集表,这种方法用一个容器存储所有的边。
9999

100100
## **练习:**
101-
实现链接表表示法的图结构。
101+
实现链接表表示法的图结构。

data-structure/linked_list.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ impl List {
4040
fn len(&self) -> u32 {
4141
// `self` 的类型是 `&List`, `*self` 的类型是 `List`,
4242
// 匹配一个类型 `T` 好过匹配一个引用 `&T`
43-
match *self {
43+
match *self {
4444
// 因为`self`是借用的,所以不能转移 tail 的所有权
4545
// 因此使用 tail 的引用
4646
Cons(_, ref tail) => 1 + tail.len(),
@@ -84,4 +84,4 @@ fn main() {
8484
基于以上代码实现一个双向循环链表。
8585

8686
>双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向循环链表。
87-
>循环链表是另一种形式的链式存贮结构。它的特点是表中最后一个结点的指针域指向头结点,整个链表形成一个环。
87+
>循环链表是另一种形式的链式存贮结构。它的特点是表中最后一个结点的指针域指向头结点,整个链表形成一个环。

data-structure/queue.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ impl <T> Queue<T> {
2323

2424
fn push(&mut self, item:T) {
2525
self.qdata.push(item);
26-
}
26+
}
2727

2828
fn pop(&mut self) -> T{
2929
self.qdata.remove(0)

0 commit comments

Comments
 (0)