Skip to content

Commit

Permalink
Merge pull request #521 from wayslog/master
Browse files Browse the repository at this point in the history
修复大量来自gitbook的错误积累
wayslog authored Jan 1, 2017

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
2 parents 82c8b1b + 870291f commit 9a0c898
Showing 15 changed files with 65 additions and 64 deletions.
2 changes: 1 addition & 1 deletion SUMMARY.md
Original file line number Diff line number Diff line change
@@ -100,7 +100,7 @@
* [评测 (benchmark)](testing/bench.md)
* [代码风格](coding-style/style.md)「tiansiyuan」
* [Any与反射](any/any.md)「wayslog」
* [安全(safe)](safe/safety.md)「daogangtang」
* [安全](safe/safety.md)「daogangtang」
* [常用数据结构实现](data-structure/preface.md)「Naupio」
* [栈结构](data-structure/stack.md)
* [队列](data-structure/queue.md)
16 changes: 8 additions & 8 deletions action/db/readme.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# rust数据库操作

编程时,我们依赖数据库来存储相应的数据,很多编程语言都支持对数据库的操作(为什么不是全部,因为还有html这样的东西),所以当然
可以使用rust操作数据库。
编程时,我们依赖数据库来存储相应的数据,很多编程语言都支持对数据库的操作,所以当然可以使用Rust操作数据库。

不过在我自己操作时,发现很多问题,主要因为我不了解rust在操作数据库时,应该注意的事情,从而浪费了很多的时间,在进行数据查询时。
不过在我自己操作时,发现很多问题,主要因为我不了解Rust在操作数据库时,应该注意的事情,从而浪费了很多的时间,在进行数据查询时。
具体遇到的坑,我会做一些演示,从而让大家避免这些情况。

首先使用rust操作postgresql,因为postgresql是我最喜欢的数据库
首先使用Rust操作PostgreSQL,因为PostgreSQL是我最喜欢的数据库

首先创建新项目 `cargo new db --bin`

在cargo.toml中添加,postgres:
在cargo.toml中添加 `postgres` 如下:


``` rust
[package]
@@ -185,7 +185,7 @@ fn main() {
自己遇到的坑

- 创建连接函数时,连接必须有一个返回值,所以必须指定返回值的类型,
对于一个写python的人而言,我觉得是痛苦的,我想按照官方的写法match
对于一个写Python的人而言,我觉得是痛苦的,我想按照官方的写法match
一下,发现可能产生多个返回值。在编译时直接无法通过编译,所以最终
使用了unwrap,解决问题,不过我还是没有学会,函数多值返回时我如何
定义返回值
@@ -224,8 +224,8 @@ Could not compile `db`.

然后去查看了关于postgres模块的所有函数,尝试了无数种办法,依旧没有解决。

可能自己眼高手低,如果从头再把rust的相关教程看一下,可能很早就发现这个问题,
也有可能是因为习惯了写python,导致自己使用固有的思维来看待问题和钻牛角尖,才
可能自己眼高手低,如果从头再把Rust的相关教程看一下,可能很早就发现这个问题,
也有可能是因为习惯了写Python,导致自己使用固有的思维来看待问题和钻牛角尖,才
导致出现这样的问题,浪费很多的时间。

- 改变思维,把自己当作一个全新的新手,既要利用已有的思想来学习新的语言,同样不要
10 changes: 5 additions & 5 deletions action/json_data/readme.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# rust json处理
# Rust json处理

json是一种比较重要的格式,尤其是现在的web开发领域,json相比于传统的xml更加容易操作和减小传输
JSON是一种比较重要的格式,尤其是现在的web开发领域,JSON相比于传统的XML更加容易操作和减小传输

rust中的json处理依赖 cargo 中的rustc-serialize模块
Rust中的JSON处理依赖 cargo 中的rustc-serialize模块

###先简单的创建一个rust项目工程
###先简单的创建一个Rust项目工程

``` rust
$ cargo new json_data --bin
@@ -64,7 +64,7 @@ $ cargo build
*注意一个问题由于国内网络访问github不稳定,这些第三方库很多托管在github上,所以可能需要修改你的
网络访问*
1. 在安装rust之后,会在你的用户目录之下生成一个`.cargo`文件夹,进入这个文件夹
1. 在安装Rust之后,会在你的用户目录之下生成一个`.cargo`文件夹,进入这个文件夹
2. 在`.cargo`文件夹下,创建一个`config`文件,在文件中填写中科大软件源,可能以后会出现其他的源,先用这个
3. `config`文件内容如下
2 changes: 1 addition & 1 deletion ffi/compiling-rust-to-lib.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 将rust编译成库
# 将Rust编译成库
上一章讲述了如何从rust中调用c库,这一章我们讲如何把rust编译成库让别的语言通过cffi调用。

## 调用约定和mangle
4 changes: 2 additions & 2 deletions iterator/iterator.md
Original file line number Diff line number Diff line change
@@ -128,7 +128,7 @@ let m = (1..20).fold(1u64, |mul, x| mul*x);

### 适配器

我们所熟知的生产消费的模型里,生产者所生产的东西不一定都会被消费者买账,因此,需要对原有的产品进行再组装。这个再组装的过程,就是适配器。因为适配器返回的是一个新的迭代器,可以直接用链式请求一直写下去,而不至于陷入到某前端语言的回调地狱之中
我们所熟知的生产消费的模型里,生产者所生产的东西不一定都会被消费者买账,因此,需要对原有的产品进行再组装。这个再组装的过程,就是适配器。因为适配器返回的是一个新的迭代器,所以可以直接用链式请求一直写下去

前面提到了 Reduce 函数,那么自然不得不提一下另一个配套函数 —— `map` :

@@ -149,7 +149,7 @@ warning: unused result which must be used: iterator adaptors are lazy and

呀,这是啥?

因为,所有的适配器,都是惰性求值的!都是惰性求值的!都是惰性求值的!
因为,所有的适配器,都是惰性求值的!

**也就是说,除非你调用一个消费者,不然,你的操作,永远也不会被调用到!**

2 changes: 1 addition & 1 deletion module/module.md
Original file line number Diff line number Diff line change
@@ -263,7 +263,7 @@ use super::xxx;

路径中的 `*` 符号:
```rust
use xxx:*
use xxx::*;
```
表示导入 `xxx` 模块下的所有可见 item(加了 pub 标识的 item)。

2 changes: 1 addition & 1 deletion ownership-system/lifetime.md
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@ fn main() {

> error: unresolved name `x`.
错误的意思是“无法解析 `a` 标识符”,也就是找不到 `x` , 这是因为像很多编程语言一样,Rust中也存在作用域概念,当资源离开离开作用域后,资源的内存就会被释放回收,当借用/引用离开作用域后也会被销毁,所以 `x` 在离开自己的作用域后,无法在作用域之外访问。
错误的意思是“无法解析 `x` 标识符”,也就是找不到 `x` , 这是因为像很多编程语言一样,Rust中也存在作用域概念,当资源离开离开作用域后,资源的内存就会被释放回收,当借用/引用离开作用域后也会被销毁,所以 `x` 在离开自己的作用域后,无法在作用域之外访问。


上面的涉及到几个概念:
4 changes: 2 additions & 2 deletions ownership-system/ownership.md
Original file line number Diff line number Diff line change
@@ -82,9 +82,9 @@ c.rs:4 println!("{}", a);
在Rust中,和“绑定”概念相辅相成的另一个机制就是“转移move所有权”,意思是,**可以把资源的所有权(ownership)从一个绑定转移(move)成另一个绑定**,这个操作同样通过`let`关键字完成,和绑定不同的是,`=`两边的左值和右值均为两个标识符:
```rust
语法:
let 标识符A = 标识符B; // 把“A”绑定资源的所有权转移给“B
let 标识符A = 标识符B; // 把“B”绑定资源的所有权转移给“A
```
Move前后的内存示意如下
move前后的内存示意如下
> **Before move:**
a <=> 内存(地址:**A**,内容:"xyz")
**After move:**
4 changes: 2 additions & 2 deletions quickstart/control-flow.md
Original file line number Diff line number Diff line change
@@ -118,8 +118,7 @@ match pair {
}
```

`match`的这种解构同样适用于结构体或者枚举。如果有必要,
还可以使用`..`来忽略域或者数据:
`match`的这种解构同样适用于结构体或者枚举。如果有必要,还可以使用`..`来忽略域或者数据:

```rust
struct Point {
@@ -141,6 +140,7 @@ enum OptionalInt {
let x = OptionalInt::Value(5);

match x {
// 这里是 match 的 if guard 表达式,我们将在以后的章节进行详细介绍
OptionalInt::Value(i) if i > 5 => println!("Got an int bigger than five!"),
OptionalInt::Value(..) => println!("Got an int!"),
OptionalInt::Missing => println!("No such luck."),
4 changes: 2 additions & 2 deletions quickstart/rust-travel.md
Original file line number Diff line number Diff line change
@@ -65,8 +65,8 @@ fn main() {
> [package]
name = "hellorust"
version = "0.1."
authors = ["YourName <YourEmail>"]
> [dependencies]
authors = ["YourName <YourEmail>"]
[dependencies]

- 编辑src目录下的main.rs文件

2 changes: 1 addition & 1 deletion quickstart/struct-enum.md
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ struct Point {
x: i32,
y: i32,
}
let mut point = Point { x: 0, y: 0 };
let point = Point { x: 0, y: 0 };

// tuple structs
struct Color(u8, u8, u8);
10 changes: 5 additions & 5 deletions testing/bench.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# 性能评测
# 性能测试

测试,是验证程序的正确性,这是第一步。程序能正常运行后,往往需要测试程序(一部分)的执行速度,这时,就需要用到性能评测
通常来讲,所谓性能评测,指的是测量程序运行的速度,即运行一次要多少时间(通常是执行多次求平均值)。Rust 竟然连这个特性都集成在语言基础特性中,真的是一门很重视工程性的语言。
测试,是验证程序的正确性,这是第一步。程序能正常运行后,往往需要测试程序(一部分)的执行速度,这时,就需要用到性能测试
通常来讲,所谓性能测试,指的是测量程序运行的速度,即运行一次要多少时间(通常是执行多次求平均值)。Rust 竟然连这个特性都集成在语言基础特性中,真的是一门很重视工程性的语言。

下面直接说明如何使用。

@@ -63,11 +63,11 @@ test tests::bench_add_two ... bench: 1 ns/iter (+/- 0)
test result: ok. 0 passed; 0 failed; 1 ignored; 1 measured
```

可以看到,Rust 的性能评测是以纳秒 ns 为单位。
可以看到,Rust 的性能测试是以纳秒 ns 为单位。

写测评代码的时候,需要注意以下一些点:

1. 只把你需要做性能评测的代码(函数)放在评测函数中;
1. 只把你需要做性能测试的代码(函数)放在评测函数中;
2. 对于参与做性能测试的代码(函数),要求每次测试做同样的事情,不要做累积和改变外部状态的操作;
3. 参数性能测试的代码(函数),执行时间不要太长。太长的话,最好分成几个部分测试。这也方便找出性能瓶颈所在地方。

63 changes: 32 additions & 31 deletions type/compound-types.md
Original file line number Diff line number Diff line change
@@ -2,15 +2,15 @@

## 元组(Tuple)

在别的语言里,你可能听过元组这个词,它表示一个大小、类型固定的有序数据组。在Rust中,情况并没有什么本质上的不同。不过Rust为我们提供了一系列简单便利的语法来让我们能更好的使用他
在别的语言里,你可能听过元组这个词,它表示一个大小、类型固定的有序数据组。在 Rust 中,情况并没有什么本质上的不同。不过 Rust 为我们提供了一系列简单便利的语法来让我们能更好的使用他

```rust
let y = (2, "hello world");
let x: (i32, &str) = (3, "world hello");

// 然后呢,你能用很简单的方式去访问他们:

// 用let表达式
// 用 let 表达式
let (w, z) = y; // w=2, z="hello world"

// 用下标
@@ -40,7 +40,7 @@ struct A {

### 元组类型结构体

元组类型结构体使用小括号,类似 `tuple`
元组类型结构体使用小括号,类似 `tuple`

```rust
struct B(i32, u16, bool);
@@ -58,8 +58,8 @@ struct D;

空结构体的内存占用为0。但是我们依然可以针对这样的类型实现它的“成员函数”。

不过到目前为止,除了1.9 nightly版本外,空结构体后面不能加大括号。
如果这么写,则会导致编译错误
不过到目前为止,在 1.9 版本之前的版本,空结构体后面不能加大括号。
如果这么写,则会导致这部分的老编译器编译错误

```rust
struct C {
@@ -98,21 +98,21 @@ fn main() {
}
```

看见了self,Python程序员不厚道的笑了。
看见了 `self`,Python程序员不厚道的笑了。

我们来分析一下,上面的`impl`中,new被Person这个结构体自身所调用,其特征是`::`的调用,Java程序员站出来了:类函数! 而带有`self``greeting`,更像是一个成员函数。
我们来分析一下,上面的`impl`中,new 被 Person 这个结构体自身所调用,其特征是 `::` 的调用,Java程序员站出来了:类函数! 而带有 `self``greeting` ,更像是一个成员函数。

恩,回答正确,然而不加分。

### 关于各种ref的讨论

Rust对代码有着严格的安全控制,因此对一个变量也就有了所有权和借用的概念。所有权同一时间只能一人持有,可变引用也只能同时被一个实例持有,不可变引用则可以被多个实例持有。同时所有权能被转移,在Rust中被称为`move`
Rust 对代码有着严格的安全控制,因此对一个变量也就有了所有权和借用的概念。所有权同一时间只能一人持有,可变引用也只能同时被一个实例持有,不可变引用则可以被多个实例持有。同时所有权能被转移,在Rust中被称为 `move`

以上是所有权的基本概念,事实上,在整个软件的运行周期内,所有权的转换是一件极其恼人和烦琐的事情,尤其对那些初学Rust的同学来说。同样的,Rust的结构体作为其类型系统的基石,也有着比较严格的所有权控制限制。具体来说,关于结构体的所有权,有两种你需要考虑的情况。
以上是所有权的基本概念,事实上,在整个软件的运行周期内,所有权的转换是一件极其恼人和烦琐的事情,尤其对那些初学 Rust 的同学来说。同样的,Rust 的结构体作为其类型系统的基石,也有着比较严格的所有权控制限制。具体来说,关于结构体的所有权,有两种你需要考虑的情况。

#### 字段的ref和owner
#### 字段的 ref 和 owner

在以上的结构体中,我们定义了不少结构体,但是如你所见,结构体的每个字段都是完整的属于自己的。也就是说,每个字段的owner都是这个结构体。每个字段的生命周期最终都不会超过这个结构体。
在以上的结构体中,我们定义了不少结构体,但是如你所见,结构体的每个字段都是完整的属于自己的。也就是说,每个字段的 owner 都是这个结构体。每个字段的生命周期最终都不会超过这个结构体。

但是有些时候,我只是想要持有一个(可变)引用的值怎么办?
如下代码:
@@ -130,7 +130,7 @@ struct RefBoy {
<anon>:6 loc: & i32,
```

这种时候,你将持有一个值的引用,因为它本身的生命周期在这个struct之外,所以对这个结构体而言,它无法准确的判断获知这个引用的生命周期,这在Rust编译器而言是不被接受的
这种时候,你将持有一个值的引用,因为它本身的生命周期在这个结构体之外,所以对这个结构体而言,它无法准确的判断获知这个引用的生命周期,这在 Rust 编译器而言是不被接受的
因此,这个时候就需要我们给这个结构体人为的写上一个生命周期,并显式地表明这个引用的生命周期。写法如下:

```rust
@@ -139,7 +139,7 @@ struct RefBoy<'a> {
}
```

这里解释一下这个符号`<>`,它表示的是一个`属于`的关系,无论其中描述的是*生命周期*还是*泛型*。即: `RefBoy in 'a `。最终我们可以得出个结论,`RefBoy`这个结构体,其生命周期一定不能比`'a`更长才行。
这里解释一下这个符号 `<>`,它表示的是一个 `属于` 的关系,无论其中描述的是 *生命周期* 还是 *泛型* 。即: `RefBoy in 'a `。最终我们可以得出个结论,`RefBoy` 这个结构体,其生命周期一定不能比 `'a` 更长才行。

写到这里,可能有的人还是对生命周期比较迷糊,不明白其中缘由,其实你只需要知道两点即可:

@@ -148,17 +148,17 @@ struct RefBoy<'a> {

关于第二点,其实生命周期是可以写多个的,用 `,` 分隔。

注:生命周期和泛型都写在`<>`里,先生命周期后泛型,用`;`分隔。
注:生命周期和泛型都写在 `<>` 里,先生命周期后泛型,用`,`分隔。

#### impl中的三种self

前面我们知道,Rust中,通过impl可以对一个结构体添加成员方法。同时我们也看到了`self`这样的关键字,同时,这个self也有好几种需要你仔细记忆的情况。

impl中的self,常见的有三种形式:`self``&self``&mut self` ,我们分别来说。
impl中的self,常见的有三种形式:`self` `&self``&mut self` ,我们分别来说。

##### 被move的self

正如上面例子中的impl,我们实现了一个以`self`为第一个参数的函数,但是这样的函数实际上是有问题的。
正如上面例子中的impl,我们实现了一个以 `self` 为第一个参数的函数,但是这样的函数实际上是有问题的。
问题在于Rust的所有权转移机制。

我曾经见过一个关于Rust的笑话:"你调用了一下别人,然后你就不属于你了"。
@@ -189,9 +189,9 @@ fn main() {
<anon>:13 println!("{}", ast.a);
```

为什么呢?因为Rust本身,在你调用一个函数的时候,如果传入的不是一个引用,那么无疑,这个参数的owner将被move掉。同理,`impl`中的`self`,如果你写的不是一个引用的话,也是会被默认的move掉哟
为什么呢?因为 Rust 本身,在你调用一个函数的时候,如果传入的不是一个引用,那么无疑,这个参数将被这个函数吃掉,即其 owner 将被 move 到这个函数的参数上。同理,`impl` 中的 `self` ,如果你写的不是一个引用的话,也是会被默认的 move 掉哟

那么如何避免这种情况呢?答案是`Copy``Clone`
那么如何避免这种情况呢?答案是 `Copy``Clone`

```rust
#[derive(Copy, Clone)]
@@ -200,13 +200,13 @@ struct A {
}
```

这么写的话,会使编译通过。但是这么写实际上也是有其缺陷的。其缺陷就是:你不能在一个被copy的`impl`函数里改变它!事实上,被move的`self`其实是相对少用的一种情况,更多的时候,我们需要的是`ref``ref mut`
这么写的话,会使编译通过。但是这么写实际上也是有其缺陷的。其缺陷就是: `Copy` 或者 `Clone` ,都会带来一定的运行时开销!事实上,被move的 `self` 其实是相对少用的一种情况,更多的时候,我们需要的是 `ref``ref mut`

###### ref和ref mut
###### ref 和 ref mut

关于`ref``mut ref`的写法和被move的`self`写法类似,只不过多了一个引用修饰符号,上面有例子,不多说。
关于 `ref``mut ref` 的写法和被 move 的 `self` 写法类似,只不过多了一个引用修饰符号,上面有例子,不多说。

需要注意的一点是,你不能在一个`ref`的方法里调用一个`mut ref`,任何情况下都不行!
需要注意的一点是,你不能在一个 `&self` 的方法里调用一个 `&mut ref` ,任何情况下都不行!

但是,反过来是可以的。代码如下:

@@ -238,15 +238,15 @@ fn main() {
}
```

需要注意的是,一旦你的结构体持有一个可变引用,你,只能在`&mut self`的实现里去改变他!
需要注意的是,一旦你的结构体持有一个可变引用,你,只能在 `&mut self` 的实现里去改变他!

Rust允许我们灵活的对一个struct进行你想要的实现,在编程的自由度上无疑有了巨大的提高。
Rust允许我们灵活的对一个 struct 进行你想要的实现,在编程的自由度上无疑有了巨大的提高。

至于更高级的关于trait和泛型的用法,我们将在以后的章节进行详细介绍。
至于更高级的关于 trait 和泛型的用法,我们将在以后的章节进行详细介绍。

## 枚举类型 enum

Rust的枚举(`enum`)类型,跟C语言的枚举有点接近,然而更强大,事实上是代数数据类型(Algebraic Data Type)。
Rust的枚举(`enum`)类型,跟C语言的枚举有点接近,然而更强大,事实上它是一种代数数据类型(Algebraic Data Type)。

比如说,这是一个代表东南西北四个方向的枚举:

@@ -259,7 +259,7 @@ enum Direction {
}
```

但是,rust的枚举能做到的,比C语言的多很多
但是,rust 的枚举能做到的,比 C 语言的更多
比如,枚举里面居然能包含一些你需要的,特定的数据信息!
这是常规的枚举所无法做到的,更像枚举类,不是么?

@@ -284,7 +284,8 @@ enum SpecialPoint {

### 使用枚举

和struct的成员访问符号`.`不同的是,枚举类型要想访问其成员,几乎无一例外的要用到模式匹配。并且, 你可以写一个`Direction::West`,但是你绝对不能写成`Direction.West`。虽然编译器足够聪明能发现你这个粗心的毛病。
和struct的成员访问符号 `.` 不同的是,枚举类型要想访问其成员,几乎无一例外的要用到模式匹配。并且, 你可以写一个 `Direction::West`,但是你现在还不能写成 `Direction.West`, 除非你显式的 `use` 它 。虽然编译器足够聪明能发现你这个粗心的毛病。


关于模式匹配,我不会说太多,还是举个栗子

@@ -308,7 +309,7 @@ fn main() {
```

呐呐呐,这就是模式匹配取值啦。
当然了,`enum`其实也是可以`impl`的,一般人我不告诉他!
当然了, `enum` 其实也是可以 `impl` 的,一般人我不告诉他!

对于带有命名字段的枚举,模式匹配时可指定字段名

@@ -321,7 +322,7 @@ match sp {
}
```

对于带有字段名的枚举类型,其模式匹配语法与匹配`struct`时一致。如
对于带有字段名的枚举类型,其模式匹配语法与匹配 `struct` 时一致。如

```rust
struct Point {
@@ -338,4 +339,4 @@ let Point { x, y } = point;
let Point { x: x, .. } = point;
```

模式匹配的语法与`if let``let`是一致的,所以在后面的内容中看到的也支持同样的语法。
模式匹配的语法与 `if let``let` 是一致的,所以在后面的内容中看到的也支持同样的语法。
2 changes: 1 addition & 1 deletion type/operator-and-formatting.md
Original file line number Diff line number Diff line change
@@ -183,7 +183,7 @@ parameter := integer '$'

最后,留个作业吧。
给出参数列表如下:
`(500.0, 12, "ELTON", "QB", 8, CaiNiao="Mike")`
`(500.0, 12, "ELTON", "QB", 4, CaiNiao="Mike")`

请写出能最后输出一下句子并且将参数**被用过*至少一遍*的格式化字符串,并自己去play实验一下。

2 changes: 1 addition & 1 deletion type/string.md
Original file line number Diff line number Diff line change
@@ -59,7 +59,7 @@ fn main() {

我们来分析一下,以下部分将涉及到部分`Deref`的知识,可能需要你预习一下,如果不能理解大可跳过下一段:

首先呢, `&*`是两个符号`&``*`的集合,按照Rust的运算顺序,先对`String`进行`Deref`,也就是`*`操作。
首先呢, `&*`是两个符号`&``*`的组合,按照Rust的运算顺序,先对`String`进行`Deref`,也就是`*`操作。

由于`String`实现了 `impl Deref<Target=str> for String`,这相当于一个运算符重载,所以你就能通过`*`获得一个`str`类型。但是我们知道,单独的`str`是不能在Rust里直接存在的,因此,我们需要先给他进行`&`操作取得`&str`这个结果。

0 comments on commit 9a0c898

Please sign in to comment.