Skip to content

Commit

Permalink
Merge pull request #354 from wayslog/master
Browse files Browse the repository at this point in the history
reformat as double blank in line tail
  • Loading branch information
WaySLOG committed May 4, 2016
2 parents bd39a40 + dac46df commit c5cc061
Show file tree
Hide file tree
Showing 10 changed files with 72 additions and 65 deletions.
13 changes: 9 additions & 4 deletions concurrency-parallel-thread/message-passing.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ use std::thread;

fn main() {
// 创建一个通道
let (tx, rx): (mpsc::Sender<i32>, mpsc::Receiver<i32>) = mpsc::channel();
let (tx, rx): (mpsc::Sender<i32>, mpsc::Receiver<i32>) =
mpsc::channel();

// 创建线程用于发送消息
thread::spawn(move || {
Expand Down Expand Up @@ -60,7 +61,8 @@ impl fmt::Display for Student {

fn main() {
// 创建一个通道
let (tx, rx): (mpsc::Sender<Rc<Student>>, mpsc::Receiver<Rc<Student>>) = mpsc::channel();
let (tx, rx): (mpsc::Sender<Rc<Student>>, mpsc::Receiver<Rc<Student>>) =
mpsc::channel();

// 创建线程用于发送消息
thread::spawn(move || {
Expand All @@ -76,7 +78,8 @@ fn main() {
```
编译代码,奇迹没有出现,编译时错误,错误提示:
```
error: the trait `core::marker::Send` is not implemented for the type `alloc::rc::Rc<Student>` [E0277]
error: the trait `core::marker::Send` is not
implemented for the type `alloc::rc::Rc<Student>` [E0277]
note: `alloc::rc::Rc<Student>` cannot be sent between threads safely
```
看来并不是所有类型的消息都可以通过通道发送,消息类型必须实现`marker trait Send`。Rust之所以这样强制要求,主要是为了解决并发安全的问题,再一次强调,**安全**是Rust考虑的重中之重。如果一个类型是`Send`,则表明它可以在线程间安全的转移所有权(`ownership`),当所有权从一个线程转移到另一个线程后,同一时间就只会存在一个线程能访问它,这样就避免了数据竞争,从而做到线程安全。`ownership`的强大又一次显示出来了。通过这种做法,在编译时即可要求所有的代码必须满足这一约定,这种方式方法值得借鉴,`trait`也是非常强大。
Expand Down Expand Up @@ -143,7 +146,9 @@ receive 2
在代码中,我们故意让`main`所在的主线程睡眠2秒,从而让发送者所在线程优先执行,通过结果可以发现,发送者发送消息时确实没有阻塞。还记得在前面提到过很多关于通道的问题吗?从这个例子里面还发现什么没?除了不阻塞之外,我们还能发现另外的三个特征:

1. 通道是可以同时支持多个发送者的,通过`clone`的方式来实现。
这类似于`Rc`的共享机制,其实从`Channel`所在的库名`std::sync::mpsc`也可以知道这点,因为`mpsc`就是多生产者单消费者(Multiple Producers Single Consumer)的简写。
这类似于`Rc`的共享机制。
其实从`Channel`所在的库名`std::sync::mpsc`也可以知道这点。
因为`mpsc`就是多生产者单消费者(Multiple Producers Single Consumer)的简写。
可以有多个发送者,但只能有一个接收者,即支持的N:1模式。

2. 异步通道具备消息缓存的功能,因为1和2是在没有接收之前就发了的,在此之后还能接收到这两个消息。
Expand Down
14 changes: 7 additions & 7 deletions data-structure/stack.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,18 @@ struct StackNode<T> {
>现在我们重点来看看第四行:
我们**从里到外**拆分来看看,首先是`Box<StackNode<T>`,这里的`Box`是 Rust 用来显式分配堆内存的类型:
> `pub struct Box<T> where T: ?Sized(_);`
> `pub struct Box<T> where T: ?Sized(_);`
[详细文档请参考Rust的标准库](http://doc.rust-lang.org/nightly/std/boxed/struct.Box.html)

> 在 Rust 里面用强大的类型系统做了统一的抽象。在这里相当于在堆空间里申请了一块内存保存`StackNode<T>`
> 在 Rust 里面用强大的类型系统做了统一的抽象。在这里相当于在堆空间里申请了一块内存保存`StackNode<T>`
> **为什么要这么做了?如果不用Box封装会怎么样呢?**
> **为什么要这么做了?如果不用Box封装会怎么样呢?**
> 如果不用 Box 封装,rustc 编译器会报错,在 Rust 里面,rustc 默认使用栈空间,但是这里的`StackNode`定义的时候使用了递归的数据结构,next 属性的类型是 `StackNode<T>`,而这个类型是无法确定大小的,所有这种无法确定大小的类型,都不能保存在栈空间。所以需要使用`Box`来封装。这样的话`next`的类型就是一个指向某一块堆空间的指针,而指针是可以确定大小的,因此能够保存在栈空间。
> 如果不用 Box 封装,rustc 编译器会报错,在 Rust 里面,rustc 默认使用栈空间,但是这里的`StackNode`定义的时候使用了递归的数据结构,next 属性的类型是 `StackNode<T>`,而这个类型是无法确定大小的,所有这种无法确定大小的类型,都不能保存在栈空间。所以需要使用`Box`来封装。这样的话`next`的类型就是一个指向某一块堆空间的指针,而指针是可以确定大小的,因此能够保存在栈空间。
> **那么为什么还需要使用`Option`来封装呢?**
> **那么为什么还需要使用`Option`来封装呢?**
> `Option`是 Rust 里面的一个抽象类型,定义如下:
> `Option`是 Rust 里面的一个抽象类型,定义如下:
>
```rust
pub enum Option<T> {
Expand Down Expand Up @@ -103,7 +103,7 @@ impl<T> Stack<T> {
- `new( )`比较简单,Stack 初始化的时候为空,栈顶元素 `top` 就没有任何值,所以 `top``None`

- `push( )`的主要功能是往栈里面推入元素,把新的 StackNode 指向 Stack 里面旧的值,同时更新 Stack 栈顶指向新进来的值。
> 这里有个需要注意的地方是第8行代码里面,`let next = self.top.take();`,使用了 Option 类型的 take 方法:
> 这里有个需要注意的地方是第8行代码里面,`let next = self.top.take();`,使用了 Option 类型的 take 方法:
`fn take(&mut self) -> Option<T>`
它会把 Option 类型的值取走,并把它的元素改为 None

Expand Down
5 changes: 4 additions & 1 deletion flow/repetition.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,10 @@ i = 4 and j = 9
再比如:

```rust
let lines = "Content of line one\nContent of line two\nContent of line three\nContent of line four".lines();
let lines = "Content of line one
Content of line two
Content of line three
Content of line four".lines();
for (linenumber, line) in lines.enumerate() {
println!("{}: {}", linenumber, line);
}
Expand Down
21 changes: 10 additions & 11 deletions generic/generic.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
泛型
===================

----------
我们在编程中,通常有这样的需求,为多种类型的数据编写一个功能相同的函数,如两个数的加法,希望这个函数既支持i8、i16、 i32 ....float64等等,甚至自定义类型,在不支持泛型的编程语言中,我们通常要为每一种类型都编写一个函数,而且通常情况下函数名还必须不同,例如:
```rust
fn add_i8(a:i8, b:i8) -> i8 {
Expand Down Expand Up @@ -79,9 +78,9 @@ fn main() {
}
```

>**输出:**
>101
>200.33
>**输出:**
>101
>200.33
```add<T: Add<T, Output=T>>(a:T, b:T) -> T```就是我们泛型函数,返回值也是泛型T,Add<>中的含义可以暂时忽略,大体意思就是只要参数类型实现了Add trait,就可以被传入到我们的add函数,因为我们的add函数中有相加+操作,所以要求传进来的参数类型必须是可相加的,也就是必须实现了Add trait(具体参考std::ops::Add)。

Expand Down Expand Up @@ -120,10 +119,10 @@ fn main() {
println!("{:?}", add(p1, p2));
}
```
>**输出:**
>101
200.33
Point { x: 3, y: 3 }
>**输出:**
>101
200.33
Point { x: 3, y: 3 }

上面的例子稍微更复杂些了,只是我们增加了自定义的类型,然后让add函数依然可以在上面工作。如果对trait不熟悉,请查阅trait相关章节。

Expand Down Expand Up @@ -163,9 +162,9 @@ fn main() {
}
```

>**输出:**
>Point { x: 3.2, y: 3.2 }
Point { x: 3, y: 3 }
>**输出:**
>Point { x: 3.2, y: 3.2 }
Point { x: 3, y: 3 }

上面的列子更复杂了些,我们不仅让自定义的Point类型支持了add操作,同时我们也为Point做了泛型化。

Expand Down
2 changes: 1 addition & 1 deletion match/pattern.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ x: 1

在以上代码中,match作用域里的`x`这个变量被覆盖成了`'c'`,而出了这个作用域之后,变量又恢复成了原来的值。这和变量绑定的行为是一致的。

## 更强大的结构
## 更强大的解构

在上一节里,我们初步了解了模式匹配在解构`enum`时候的便利性,事实上,在Rust中模式可以被用来对任何复合类型进行解构——struct/tuple/enum。现在我们要进行一个复杂点的例子,对`struct`进行解构。

Expand Down
30 changes: 15 additions & 15 deletions ownership-system/ownership.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,11 @@ c.rs:4 println!("{}", a);
let 标识符A = 标识符B; // 把“A”绑定资源的所有权转移给“B”
```
Move前后的内存示意如下:
> **Before move:**
a <=> 内存(地址:**A**,内容:"xyz")
**After move:**
a
b <=> 内存(地址:**A**,内容:"xyz")
> **Before move:**
a <=> 内存(地址:**A**,内容:"xyz")
**After move:**
a
b <=> 内存(地址:**A**,内容:"xyz")

被move的变量不可以继续被使用。否则提示错误`error: use of moved value`

Expand All @@ -106,11 +106,11 @@ b <=> 内存(地址:**A**,内容:"xyz")
```
编译确实可以通过,输出为`100`。这是为什么呢,是不是跟move小节里的结论相悖了?
其实不然,这其实是根据变量类型是否实现`Copy`特性决定的。对于实现`Copy`特性的变量,在move时会拷贝资源到新内存区域,并把新内存区域的资源`binding``b`
> **Before move:**
a <=> 内存(地址:**A**,内容:100)
**After move:**
a <=> 内存(地址:**A**,内容:100)
b <=> 内存(地址:**B**,内容:100)
> **Before move:**
a <=> 内存(地址:**A**,内容:100)
**After move:**
a <=> 内存(地址:**A**,内容:100)
b <=> 内存(地址:**B**,内容:100)

move前后的`a``b`对应资源内存的地址不同。

Expand All @@ -129,11 +129,11 @@ move前后的`a`和`b`对应资源内存的地址不同。
这个时候可以编译通过,并且成功打印"xyz"。

clone后的效果等同如下:
> **Before move:**
a <=> 内存(地址:**A**,内容:"xyz")
**After move:**
a <=> 内存(地址:**A**,内容:"xyz")
b <=> 内存(地址:**B**,内容:"xyz")
> **Before move:**
a <=> 内存(地址:**A**,内容:"xyz")
**After move:**
a <=> 内存(地址:**A**,内容:"xyz")
b <=> 内存(地址:**B**,内容:"xyz")
注意,然后a和b对应的资源值相同,但是内存地址并不一样。


Expand Down
6 changes: 3 additions & 3 deletions quickstart/primitive-type.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ fn main() {

**例如:**
固定大小类型:
> 1u8 1i8
> 1u16 1i16
> 1u32 1i32
> 1u8 1i8
> 1u16 1i16
> 1u32 1i32
> 1u64 1i64
可变大小类型:
Expand Down
42 changes: 21 additions & 21 deletions quickstart/rust-travel.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@

- 创建一个 Doing 目录和 helloworld.rs 文件

> ps: mkdir ~/Doing
> ps: cd ~/Doing
> ps: notepad helloworld.rs # 作者偏向于使用 sublime 作为编辑器
> ps: subl helloworld.rs # 本章以后使用 subl 代替 notepad
> ps: mkdir ~/Doing
> ps: cd ~/Doing
> ps: notepad helloworld.rs # 作者偏向于使用 sublime 作为编辑器
> ps: subl helloworld.rs # 本章以后使用 subl 代替 notepad
注意这里用的后缀名是.rs,一般编程语言的代码文件都有惯用的后缀名,比如:
C语言是.c,java是.java,python是.py等等,**请务必记住Rust语言的惯用后缀名是.rs**(虽然用别的后缀名也能通过rustc的编译)。
Expand All @@ -26,13 +26,13 @@ fn main() {

- 编译 helloworld.rs 文件

> ps: rustc helloworld.rs
> ps: rustc helloworld.rs -O # 也可以选择优化编译
> ps: rustc helloworld.rs
> ps: rustc helloworld.rs -O # 也可以选择优化编译
- 运行程序

> ps: ./helloworld.exe # windows 平台下需要加 .exe 后缀
> Hello World!
> ps: ./helloworld.exe # windows 平台下需要加 .exe 后缀
> Hello World!
没有`ps:`前缀的表示为控制台打印输出。

Expand All @@ -53,20 +53,20 @@ fn main() {
- 查看目录结构

> ps: tree # win10 powershell 自带有 tree 查看文件目录结构的功能
> └─hellorust
> ps: tree # win10 powershell 自带有 tree 查看文件目录结构的功能
> └─hellorust
> ----└─src
这里显示的目录结构,在hellorust目录下有 src 文件夹和 Cargo.toml 文件,同时这个目录会初始化为 git 项目

- 查看Cargo.toml文件

> ps: cat Cargo.toml
> [package]
name = "hellorust"
version = "0.1."
> ps: cat Cargo.toml
> [package]
name = "hellorust"
version = "0.1."
authors = ["YourName <YourEmail>"]
> [dependencies]
> [dependencies]
- 编辑src目录下的main.rs文件

Expand All @@ -90,9 +90,9 @@ fn main() {

- 编译和运行

> ps: cargo build
> ps: cargo build --release # 这个属于优化编译
> ps: ./target/debug/hellorust.exe
> ps: ./target/release/hellorust.exe # 如果前面是优化编译,则这样运行
> ps: cargo run # 编译和运行合在一起
> ps: cargo run --release # 同上,区别是是优化编译的
> ps: cargo build
> ps: cargo build --release # 这个属于优化编译
> ps: ./target/debug/hellorust.exe
> ps: ./target/release/hellorust.exe # 如果前面是优化编译,则这样运行
> ps: cargo run # 编译和运行合在一起
> ps: cargo run --release # 同上,区别是是优化编译的
2 changes: 2 additions & 0 deletions quickstart/trait.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,4 +202,6 @@ impl Graph for SimpleGraph {

let graph = SimpleGraph;
let object = Box::new(graph) as Box<Graph<N=Node, E=Edge>>;

```

2 changes: 0 additions & 2 deletions rcarc/rcarc.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,11 @@ use std::rc::Rc;

struct Owner {
name: String
// ...other fields
}

struct Gadget {
id: i32,
owner: Rc<Owner>
// ...other fields
}

fn main() {
Expand Down

0 comments on commit c5cc061

Please sign in to comment.