diff --git a/docs/ch01-00-introduction.html b/docs/ch01-00-introduction.html index 2a2bff6..8b8b49e 100644 --- a/docs/ch01-00-introduction.html +++ b/docs/ch01-00-introduction.html @@ -47,7 +47,7 @@
diff --git a/docs/ch01-01-installation.html b/docs/ch01-01-installation.html index c2a54ae..a7a4c3c 100644 --- a/docs/ch01-01-installation.html +++ b/docs/ch01-01-installation.html @@ -47,7 +47,7 @@
diff --git a/docs/ch01-02-hello-world.html b/docs/ch01-02-hello-world.html index ff321a9..234d9b8 100644 --- a/docs/ch01-02-hello-world.html +++ b/docs/ch01-02-hello-world.html @@ -47,7 +47,7 @@
diff --git a/docs/ch02-00-guessing-game-tutorial.html b/docs/ch02-00-guessing-game-tutorial.html index 5f6cde5..6e4b961 100644 --- a/docs/ch02-00-guessing-game-tutorial.html +++ b/docs/ch02-00-guessing-game-tutorial.html @@ -47,7 +47,7 @@
diff --git a/docs/ch03-00-common-programming-concepts.html b/docs/ch03-00-common-programming-concepts.html index 690f15c..df22a20 100644 --- a/docs/ch03-00-common-programming-concepts.html +++ b/docs/ch03-00-common-programming-concepts.html @@ -47,7 +47,7 @@
diff --git a/docs/ch03-01-variables-and-mutability.html b/docs/ch03-01-variables-and-mutability.html index c78647f..7cd211e 100644 --- a/docs/ch03-01-variables-and-mutability.html +++ b/docs/ch03-01-variables-and-mutability.html @@ -47,7 +47,7 @@
diff --git a/docs/ch03-02-data-types.html b/docs/ch03-02-data-types.html index 92e34dc..c8b5df9 100644 --- a/docs/ch03-02-data-types.html +++ b/docs/ch03-02-data-types.html @@ -47,7 +47,7 @@
diff --git a/docs/ch03-03-how-functions-work.html b/docs/ch03-03-how-functions-work.html index 64df1f8..18f6355 100644 --- a/docs/ch03-03-how-functions-work.html +++ b/docs/ch03-03-how-functions-work.html @@ -47,7 +47,7 @@
diff --git a/docs/ch03-04-comments.html b/docs/ch03-04-comments.html index 1ebe74c..aad56ff 100644 --- a/docs/ch03-04-comments.html +++ b/docs/ch03-04-comments.html @@ -47,7 +47,7 @@
diff --git a/docs/ch03-05-control-flow.html b/docs/ch03-05-control-flow.html index b1567f1..d755b7c 100644 --- a/docs/ch03-05-control-flow.html +++ b/docs/ch03-05-control-flow.html @@ -47,7 +47,7 @@
diff --git a/docs/ch04-00-understanding-ownership.html b/docs/ch04-00-understanding-ownership.html index 858257f..40d79f6 100644 --- a/docs/ch04-00-understanding-ownership.html +++ b/docs/ch04-00-understanding-ownership.html @@ -47,7 +47,7 @@
diff --git a/docs/ch04-01-what-is-ownership.html b/docs/ch04-01-what-is-ownership.html index bdd9f33..8c2f688 100644 --- a/docs/ch04-01-what-is-ownership.html +++ b/docs/ch04-01-what-is-ownership.html @@ -47,7 +47,7 @@
diff --git a/docs/ch04-02-references-and-borrowing.html b/docs/ch04-02-references-and-borrowing.html index 8b45cc6..22e72c5 100644 --- a/docs/ch04-02-references-and-borrowing.html +++ b/docs/ch04-02-references-and-borrowing.html @@ -47,7 +47,7 @@
diff --git a/docs/ch04-03-slices.html b/docs/ch04-03-slices.html index 3e501c4..5426622 100644 --- a/docs/ch04-03-slices.html +++ b/docs/ch04-03-slices.html @@ -47,7 +47,7 @@
diff --git a/docs/ch05-00-structs.html b/docs/ch05-00-structs.html index cc1b858..7f6ca26 100644 --- a/docs/ch05-00-structs.html +++ b/docs/ch05-00-structs.html @@ -47,7 +47,7 @@
diff --git a/docs/ch05-01-method-syntax.html b/docs/ch05-01-method-syntax.html index d6e509b..a829bcf 100644 --- a/docs/ch05-01-method-syntax.html +++ b/docs/ch05-01-method-syntax.html @@ -47,7 +47,7 @@
diff --git a/docs/ch06-00-enums.html b/docs/ch06-00-enums.html index 890dde4..b08e5a7 100644 --- a/docs/ch06-00-enums.html +++ b/docs/ch06-00-enums.html @@ -47,7 +47,7 @@
diff --git a/docs/ch06-01-defining-an-enum.html b/docs/ch06-01-defining-an-enum.html index 05bb1d8..931db5c 100644 --- a/docs/ch06-01-defining-an-enum.html +++ b/docs/ch06-01-defining-an-enum.html @@ -47,7 +47,7 @@
diff --git a/docs/ch06-02-match.html b/docs/ch06-02-match.html index 2b3b06a..3650771 100644 --- a/docs/ch06-02-match.html +++ b/docs/ch06-02-match.html @@ -47,7 +47,7 @@
diff --git a/docs/ch06-03-if-let.html b/docs/ch06-03-if-let.html index 5e32952..d3c72b7 100644 --- a/docs/ch06-03-if-let.html +++ b/docs/ch06-03-if-let.html @@ -47,7 +47,7 @@
diff --git a/docs/ch07-00-modules.html b/docs/ch07-00-modules.html index 8f73d7a..7c4b0d8 100644 --- a/docs/ch07-00-modules.html +++ b/docs/ch07-00-modules.html @@ -47,7 +47,7 @@
diff --git a/docs/ch07-01-mod-and-the-filesystem.html b/docs/ch07-01-mod-and-the-filesystem.html index b341fe2..ab25694 100644 --- a/docs/ch07-01-mod-and-the-filesystem.html +++ b/docs/ch07-01-mod-and-the-filesystem.html @@ -47,7 +47,7 @@
diff --git a/docs/ch07-02-controlling-visibility-with-pub.html b/docs/ch07-02-controlling-visibility-with-pub.html index 6fb98b4..4e5b3d6 100644 --- a/docs/ch07-02-controlling-visibility-with-pub.html +++ b/docs/ch07-02-controlling-visibility-with-pub.html @@ -47,7 +47,7 @@
diff --git a/docs/ch07-03-importing-names-with-use.html b/docs/ch07-03-importing-names-with-use.html index 2118ab4..522badb 100644 --- a/docs/ch07-03-importing-names-with-use.html +++ b/docs/ch07-03-importing-names-with-use.html @@ -47,7 +47,7 @@
@@ -67,12 +67,180 @@
-

使用use导入命名空间

+

导入命名

ch07-03-importing-names-with-use.md
commit e2a129961ae346f726f8b342455ec2255cdfed68

+

我们已经讲到了如何使用模块名称作为调用的一部分,来调用模块中的函数,如列表 7-6 中所示的nested_modules函数调用。

+
+Filename: src/main.rs +
pub mod a {
+    pub mod series {
+        pub mod of {
+            pub fn nested_modules() {}
+        }
+    }
+}
+
+fn main() {
+    a::series::of::nested_modules();
+}
+
+
+

Listing 7-6: Calling a function by fully specifying its enclosing module’s +namespaces

+
+
+

如你所见,指定函数的完全限定名称可能会非常冗长。所幸 Rust 有一个关键字使得这些调用显得更简洁。

+

使用use的简单导入

+

Rust 的use关键字的工作是缩短冗长的函数调用,通过将想要调用的函数所在的模块引入到作用域中。这是一个将a::series::of模块导入一个二进制 crate 的根作用域的例子:

+

Filename: src/main.rs

+
pub mod a {
+    pub mod series {
+        pub mod of {
+            pub fn nested_modules() {}
+        }
+    }
+}
+
+use a::series::of;
+
+fn main() {
+    of::nested_modules();
+}
+
+

use a::series::of;这一行的意思是每当想要引用of模块时,不用使用完整的a::series::of路径,可以直接使用of

+

use关键字只将指定的模块引入作用域;它并不会将其子模块也引入。这就是为什么想要调用nested_modules函数时仍然必须写成of::nested_modules

+

也可以将函数本身引入到作用域中,通过如下在use中指定函数的方式:

+
pub mod a {
+    pub mod series {
+        pub mod of {
+            pub fn nested_modules() {}
+        }
+    }
+}
+
+use a::series::of::nested_modules;
+
+fn main() {
+    nested_modules();
+}
+
+

这使得我们可以忽略所有的模块并直接引用函数。

+

因为枚举也像模块一样组成了某种命名空间,也可以使用use来导入枚举的成员。对于任何类型的use语句,如果从一个命名空间导入多个项,可以使用大括号和逗号来列举他们,像这样:

+
enum TrafficLight {
+    Red,
+    Yellow,
+    Green,
+}
+
+use TrafficLight::{Red, Yellow};
+
+fn main() {
+    let red = Red;
+    let yellow = Yellow;
+    let green = TrafficLight::Green; // because we didn’t `use` TrafficLight::Green
+}
+
+

使用*的全局引用导入

+

为了一次导入某个命名空间的所有项,可以使用*语法。例如:

+
enum TrafficLight {
+    Red,
+    Yellow,
+    Green,
+}
+
+use TrafficLight::*;
+
+fn main() {
+    let red = Red;
+    let yellow = Yellow;
+    let green = Green;
+}
+
+

*被称为全局导入glob),它会导入命名空间中所有可见的项。全局导入应该保守的使用:他们是方便的,但是也可能会引入多于你预期的内容从而导致命名冲突。

+

使用super访问父模块

+

正如我们已经知道的,当创建一个库 crate 时,Cargo 会生成一个tests模块。现在让我们来深入了解一下。在communicator项目中,打开 src/lib.rs

+

Filename: src/lib.rs

+
pub mod client;
+
+pub mod network;
+
+#[cfg(test)]
+mod tests {
+    #[test]
+    fn it_works() {
+    }
+}
+
+

第十二章会更详细的解释测试,不过其部分内容现在应该可以理解了:有一个叫做tests的模块紧邻其他模块,同时包含一个叫做it_works的函数。即便存在一些特殊注解,tests也不过是另外一个模块!所以我们的模块层次结构看起来像这样:

+
communicator
+ ├── client
+ ├── network
+ |   └── client
+ └── tests
+
+

测试是为了检验库中的代码而存在的,所以让我们尝试在it_works函数中调用client::connect函数,即便现在不准备测试任何功能:

+

Filename: src/lib.rs

+
#[cfg(test)]
+mod tests {
+    #[test]
+    fn it_works() {
+        client::connect();
+    }
+}
+
+

使用cargo test命令运行测试:

+
$ cargo test
+   Compiling communicator v0.1.0 (file:///projects/communicator)
+error[E0433]: failed to resolve. Use of undeclared type or module `client`
+ --> src/lib.rs:9:9
+  |
+9 |         client::connect();
+  |         ^^^^^^^^^^^^^^^ Use of undeclared type or module `client`
+
+warning: function is never used: `connect`, #[warn(dead_code)] on by default
+ --> src/network/server.rs:1:1
+  |
+1 | fn connect() {
+  | ^
+
+

编译失败了,不过为什么呢?并不需要像 src/main.rs 那样将communicator::置于函数前,因为这里肯定是在communicator库 crate 之内的。之所以失败的原因是路径是相对于当前模块的,在这里就是tests。唯一的例外就是use语句,它默认是相对于 crate 根模块的。我们的tests模块需要client模块位于其作用域中!

+

那么如何在模块层次结构中回退一级模块,以便在tests模块中能够调用client::connect函数呢?在tests模块中,要么可以在开头使用双冒号来让 Rust 知道我们想要从根模块开始并列出整个路径:

+
::client::connect();
+
+

要么可以使用super在层级中获取当前模块的上一级模块:

+
super::client::connect();
+
+

在这个例子中这两个选择看不出有多么大的区别,不过随着模块层次的更加深入,每次都从根模块开始就会显得很长了。在这些情况下,使用super来获取当前模块的同级模块是一个好的捷径。再加上,如果在代码中的很多地方指定了从根开始的路径,那么当通过移动子树或到其他位置来重新排列模块时,最终就需要更新很多地方的路径,这就非常乏味无趣了。

+

在每一个测试中总是不得不编写super::也会显得很恼人,不过你已经见过解决这个问题的利器了:usesuper::的功能改变了提供给use的路径,使其不再相对于根模块而是相对于父模块。

+

为此,特别是在tests模块,use super::something是常用的手段。所以现在的测试看起来像这样:

+

Filename: src/lib.rs

+
#[cfg(test)]
+mod tests {
+    use super::client;
+
+    #[test]
+    fn it_works() {
+        client::connect();
+    }
+}
+
+

如果再次运行cargo test,测试将会通过而且测试结果输出的第一部分将会是:

+
$ cargo test
+   Compiling communicator v0.1.0 (file:///projects/communicator)
+     Running target/debug/communicator-92007ddb5330fa5a
+
+running 1 test
+test tests::it_works ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+

总结

+

现在你掌握了组织代码的核心科技!利用他们将相关的代码组合在一起、防止代码文件过长并将一个整洁的公有 API 展现给库的用户。

+

接下来,让我们看看一些标准库提供的集合数据类型,你可以利用他们编写出漂亮整洁的代码。

@@ -84,6 +252,10 @@ commit e2a129961ae346f726f8b342455ec2255cdfed68

+ +
@@ -94,6 +266,10 @@ commit e2a129961ae346f726f8b342455ec2255cdfed68

+ +
diff --git a/docs/ch08-00-common-collections.html b/docs/ch08-00-common-collections.html new file mode 100644 index 0000000..bba4cce --- /dev/null +++ b/docs/ch08-00-common-collections.html @@ -0,0 +1,130 @@ + + + + + Rust 程序设计语言 中文版 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + +
+

通用集合类型

+
+

ch08-00-common-collections.md +
+commit 0d229cc5a3da341196e15a6761735b2952281569

+
+

Rust 标准库中包含一系列被称为集合collections)的非常有用的数据结构。大部分其他数据类型都代表一个特定的值,不过集合可以包含多个值。不同于内建的数组和元组类型,这些集合指向的数据是储存在堆上的,这意味着数据的数量不必在编译时就可知并且可以随着程序的运行增长或缩小。每种集合都有着不同能力和代价,而为所处的场景选择合适的集合则是你将要始终发展的技能。在这一章里,我们将详细的了解三个在 Rust 程序中被广泛使用的集合:

+
    +
  • vector 允许我们一个挨着一个地储存一系列数量可变的值
  • +
  • 字符串string)是一个字符的集合。我们之前见过String类型,现在将详细介绍它。
  • +
  • 哈希 maphash map)允许我们将值与一个特定的键(key)相关联。这是一个叫做 map 的更通用的数据结构的特定实现。
  • +
+

对于标准库提供的其他类型的集合,请查看文档

+

我们将讨论如何创建和更新 vector、字符串和哈希 map,以及他们何以如此特殊。

+ +
+ + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + diff --git a/docs/ch08-01-vectors.html b/docs/ch08-01-vectors.html new file mode 100644 index 0000000..1499015 --- /dev/null +++ b/docs/ch08-01-vectors.html @@ -0,0 +1,116 @@ + + + + + Rust 程序设计语言 中文版 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + +
+ +
+ + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + diff --git a/docs/ch08-02-strings.html b/docs/ch08-02-strings.html new file mode 100644 index 0000000..a76d0b6 --- /dev/null +++ b/docs/ch08-02-strings.html @@ -0,0 +1,116 @@ + + + + + Rust 程序设计语言 中文版 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + +
+ +
+ + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + + diff --git a/docs/ch08-03-hash-maps.html b/docs/ch08-03-hash-maps.html new file mode 100644 index 0000000..ba7e3e0 --- /dev/null +++ b/docs/ch08-03-hash-maps.html @@ -0,0 +1,108 @@ + + + + + Rust 程序设计语言 中文版 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + +
+ +
+ + + + + + + + +
+ + + + + + + +
+ + + + + + + + + + + + diff --git a/docs/index.html b/docs/index.html index f04936f..a2b979f 100644 --- a/docs/index.html +++ b/docs/index.html @@ -46,7 +46,7 @@
diff --git a/docs/print.html b/docs/print.html index 1427cda..efb9feb 100644 --- a/docs/print.html +++ b/docs/print.html @@ -47,7 +47,7 @@
@@ -3528,12 +3528,194 @@ incorrect

请随意设计更多的实验并尝试理解他们!

接下来,让我们讨论一下使用use关键字来将项引入作用域。

-

使用use导入命名空间

+

导入命名

ch07-03-importing-names-with-use.md
commit e2a129961ae346f726f8b342455ec2255cdfed68

+

我们已经讲到了如何使用模块名称作为调用的一部分,来调用模块中的函数,如列表 7-6 中所示的nested_modules函数调用。

+
+Filename: src/main.rs +
pub mod a {
+    pub mod series {
+        pub mod of {
+            pub fn nested_modules() {}
+        }
+    }
+}
+
+fn main() {
+    a::series::of::nested_modules();
+}
+
+
+

Listing 7-6: Calling a function by fully specifying its enclosing module’s +namespaces

+
+
+

如你所见,指定函数的完全限定名称可能会非常冗长。所幸 Rust 有一个关键字使得这些调用显得更简洁。

+

使用use的简单导入

+

Rust 的use关键字的工作是缩短冗长的函数调用,通过将想要调用的函数所在的模块引入到作用域中。这是一个将a::series::of模块导入一个二进制 crate 的根作用域的例子:

+

Filename: src/main.rs

+
pub mod a {
+    pub mod series {
+        pub mod of {
+            pub fn nested_modules() {}
+        }
+    }
+}
+
+use a::series::of;
+
+fn main() {
+    of::nested_modules();
+}
+
+

use a::series::of;这一行的意思是每当想要引用of模块时,不用使用完整的a::series::of路径,可以直接使用of

+

use关键字只将指定的模块引入作用域;它并不会将其子模块也引入。这就是为什么想要调用nested_modules函数时仍然必须写成of::nested_modules

+

也可以将函数本身引入到作用域中,通过如下在use中指定函数的方式:

+
pub mod a {
+    pub mod series {
+        pub mod of {
+            pub fn nested_modules() {}
+        }
+    }
+}
+
+use a::series::of::nested_modules;
+
+fn main() {
+    nested_modules();
+}
+
+

这使得我们可以忽略所有的模块并直接引用函数。

+

因为枚举也像模块一样组成了某种命名空间,也可以使用use来导入枚举的成员。对于任何类型的use语句,如果从一个命名空间导入多个项,可以使用大括号和逗号来列举他们,像这样:

+
enum TrafficLight {
+    Red,
+    Yellow,
+    Green,
+}
+
+use TrafficLight::{Red, Yellow};
+
+fn main() {
+    let red = Red;
+    let yellow = Yellow;
+    let green = TrafficLight::Green; // because we didn’t `use` TrafficLight::Green
+}
+
+

使用*的全局引用导入

+

为了一次导入某个命名空间的所有项,可以使用*语法。例如:

+
enum TrafficLight {
+    Red,
+    Yellow,
+    Green,
+}
+
+use TrafficLight::*;
+
+fn main() {
+    let red = Red;
+    let yellow = Yellow;
+    let green = Green;
+}
+
+

*被称为全局导入glob),它会导入命名空间中所有可见的项。全局导入应该保守的使用:他们是方便的,但是也可能会引入多于你预期的内容从而导致命名冲突。

+

使用super访问父模块

+

正如我们已经知道的,当创建一个库 crate 时,Cargo 会生成一个tests模块。现在让我们来深入了解一下。在communicator项目中,打开 src/lib.rs

+

Filename: src/lib.rs

+
pub mod client;
+
+pub mod network;
+
+#[cfg(test)]
+mod tests {
+    #[test]
+    fn it_works() {
+    }
+}
+
+

第十二章会更详细的解释测试,不过其部分内容现在应该可以理解了:有一个叫做tests的模块紧邻其他模块,同时包含一个叫做it_works的函数。即便存在一些特殊注解,tests也不过是另外一个模块!所以我们的模块层次结构看起来像这样:

+
communicator
+ ├── client
+ ├── network
+ |   └── client
+ └── tests
+
+

测试是为了检验库中的代码而存在的,所以让我们尝试在it_works函数中调用client::connect函数,即便现在不准备测试任何功能:

+

Filename: src/lib.rs

+
#[cfg(test)]
+mod tests {
+    #[test]
+    fn it_works() {
+        client::connect();
+    }
+}
+
+

使用cargo test命令运行测试:

+
$ cargo test
+   Compiling communicator v0.1.0 (file:///projects/communicator)
+error[E0433]: failed to resolve. Use of undeclared type or module `client`
+ --> src/lib.rs:9:9
+  |
+9 |         client::connect();
+  |         ^^^^^^^^^^^^^^^ Use of undeclared type or module `client`
+
+warning: function is never used: `connect`, #[warn(dead_code)] on by default
+ --> src/network/server.rs:1:1
+  |
+1 | fn connect() {
+  | ^
+
+

编译失败了,不过为什么呢?并不需要像 src/main.rs 那样将communicator::置于函数前,因为这里肯定是在communicator库 crate 之内的。之所以失败的原因是路径是相对于当前模块的,在这里就是tests。唯一的例外就是use语句,它默认是相对于 crate 根模块的。我们的tests模块需要client模块位于其作用域中!

+

那么如何在模块层次结构中回退一级模块,以便在tests模块中能够调用client::connect函数呢?在tests模块中,要么可以在开头使用双冒号来让 Rust 知道我们想要从根模块开始并列出整个路径:

+
::client::connect();
+
+

要么可以使用super在层级中获取当前模块的上一级模块:

+
super::client::connect();
+
+

在这个例子中这两个选择看不出有多么大的区别,不过随着模块层次的更加深入,每次都从根模块开始就会显得很长了。在这些情况下,使用super来获取当前模块的同级模块是一个好的捷径。再加上,如果在代码中的很多地方指定了从根开始的路径,那么当通过移动子树或到其他位置来重新排列模块时,最终就需要更新很多地方的路径,这就非常乏味无趣了。

+

在每一个测试中总是不得不编写super::也会显得很恼人,不过你已经见过解决这个问题的利器了:usesuper::的功能改变了提供给use的路径,使其不再相对于根模块而是相对于父模块。

+

为此,特别是在tests模块,use super::something是常用的手段。所以现在的测试看起来像这样:

+

Filename: src/lib.rs

+
#[cfg(test)]
+mod tests {
+    use super::client;
+
+    #[test]
+    fn it_works() {
+        client::connect();
+    }
+}
+
+

如果再次运行cargo test,测试将会通过而且测试结果输出的第一部分将会是:

+
$ cargo test
+   Compiling communicator v0.1.0 (file:///projects/communicator)
+     Running target/debug/communicator-92007ddb5330fa5a
+
+running 1 test
+test tests::it_works ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
+
+

总结

+

现在你掌握了组织代码的核心科技!利用他们将相关的代码组合在一起、防止代码文件过长并将一个整洁的公有 API 展现给库的用户。

+

接下来,让我们看看一些标准库提供的集合数据类型,你可以利用他们编写出漂亮整洁的代码。

+

通用集合类型

+
+

ch08-00-common-collections.md +
+commit 0d229cc5a3da341196e15a6761735b2952281569

+
+

Rust 标准库中包含一系列被称为集合collections)的非常有用的数据结构。大部分其他数据类型都代表一个特定的值,不过集合可以包含多个值。不同于内建的数组和元组类型,这些集合指向的数据是储存在堆上的,这意味着数据的数量不必在编译时就可知并且可以随着程序的运行增长或缩小。每种集合都有着不同能力和代价,而为所处的场景选择合适的集合则是你将要始终发展的技能。在这一章里,我们将详细的了解三个在 Rust 程序中被广泛使用的集合:

+
    +
  • vector 允许我们一个挨着一个地储存一系列数量可变的值
  • +
  • 字符串string)是一个字符的集合。我们之前见过String类型,现在将详细介绍它。
  • +
  • 哈希 maphash map)允许我们将值与一个特定的键(key)相关联。这是一个叫做 map 的更通用的数据结构的特定实现。
  • +
+

对于标准库提供的其他类型的集合,请查看文档

+

我们将讨论如何创建和更新 vector、字符串和哈希 map,以及他们何以如此特殊。

diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 885dafb..577d14a 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -33,4 +33,9 @@ - [模块](ch07-00-modules.md) - [`mod`和文件系统](ch07-01-mod-and-the-filesystem.md) - [使用`pub`控制可见性](ch07-02-controlling-visibility-with-pub.md) - - [使用`use`导入命名空间](ch07-03-importing-names-with-use.md) \ No newline at end of file + - [使用`use`导入命名](ch07-03-importing-names-with-use.md) + +- [通用集合类型](ch08-00-common-collections.md) + - [vector](ch08-01-vectors.md) + - [字符串](ch08-02-strings.md) + - [哈希 map](ch08-03-hash-maps.md) \ No newline at end of file diff --git a/src/ch07-03-importing-names-with-use.md b/src/ch07-03-importing-names-with-use.md index d28233c..1d33301 100644 --- a/src/ch07-03-importing-names-with-use.md +++ b/src/ch07-03-importing-names-with-use.md @@ -1,6 +1,234 @@ -## 使用`use`导入命名空间 +## 导入命名 > [ch07-03-importing-names-with-use.md](https://github.com/rust-lang/book/blob/master/src/ch07-03-importing-names-with-use.md) >
> commit e2a129961ae346f726f8b342455ec2255cdfed68 +我们已经讲到了如何使用模块名称作为调用的一部分,来调用模块中的函数,如列表 7-6 中所示的`nested_modules`函数调用。 + +
+Filename: src/main.rs + +```rust +pub mod a { + pub mod series { + pub mod of { + pub fn nested_modules() {} + } + } +} + +fn main() { + a::series::of::nested_modules(); +} +``` + +
+ +Listing 7-6: Calling a function by fully specifying its enclosing module’s +namespaces + +
+
+ +如你所见,指定函数的完全限定名称可能会非常冗长。所幸 Rust 有一个关键字使得这些调用显得更简洁。 + +### 使用`use`的简单导入 + +Rust 的`use`关键字的工作是缩短冗长的函数调用,通过将想要调用的函数所在的模块引入到作用域中。这是一个将`a::series::of`模块导入一个二进制 crate 的根作用域的例子: + +Filename: src/main.rs + +```rust +pub mod a { + pub mod series { + pub mod of { + pub fn nested_modules() {} + } + } +} + +use a::series::of; + +fn main() { + of::nested_modules(); +} +``` + +`use a::series::of;`这一行的意思是每当想要引用`of`模块时,不用使用完整的`a::series::of`路径,可以直接使用`of`。 + +`use`关键字只将指定的模块引入作用域;它并不会将其子模块也引入。这就是为什么想要调用`nested_modules`函数时仍然必须写成`of::nested_modules`。 + +也可以将函数本身引入到作用域中,通过如下在`use`中指定函数的方式: + +```rust +pub mod a { + pub mod series { + pub mod of { + pub fn nested_modules() {} + } + } +} + +use a::series::of::nested_modules; + +fn main() { + nested_modules(); +} +``` + +这使得我们可以忽略所有的模块并直接引用函数。 + +因为枚举也像模块一样组成了某种命名空间,也可以使用`use`来导入枚举的成员。对于任何类型的`use`语句,如果从一个命名空间导入多个项,可以使用大括号和逗号来列举他们,像这样: + +```rust +enum TrafficLight { + Red, + Yellow, + Green, +} + +use TrafficLight::{Red, Yellow}; + +fn main() { + let red = Red; + let yellow = Yellow; + let green = TrafficLight::Green; // because we didn’t `use` TrafficLight::Green +} +``` + +### 使用`*`的全局引用导入 + +为了一次导入某个命名空间的所有项,可以使用`*`语法。例如: + +```rust +enum TrafficLight { + Red, + Yellow, + Green, +} + +use TrafficLight::*; + +fn main() { + let red = Red; + let yellow = Yellow; + let green = Green; +} +``` + +`*`被称为**全局导入**(*glob*),它会导入命名空间中所有可见的项。全局导入应该保守的使用:他们是方便的,但是也可能会引入多于你预期的内容从而导致命名冲突。 + +### 使用`super`访问父模块 + +正如我们已经知道的,当创建一个库 crate 时,Cargo 会生成一个`tests`模块。现在让我们来深入了解一下。在`communicator`项目中,打开 *src/lib.rs*。 + +Filename: src/lib.rs + +```rust,ignore +pub mod client; + +pub mod network; + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + } +} +``` + +第十二章会更详细的解释测试,不过其部分内容现在应该可以理解了:有一个叫做`tests`的模块紧邻其他模块,同时包含一个叫做`it_works`的函数。即便存在一些特殊注解,`tests`也不过是另外一个模块!所以我们的模块层次结构看起来像这样: + +``` +communicator + ├── client + ├── network + | └── client + └── tests +``` + +测试是为了检验库中的代码而存在的,所以让我们尝试在`it_works`函数中调用`client::connect`函数,即便现在不准备测试任何功能: + +Filename: src/lib.rs + +```rust +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + client::connect(); + } +} +``` + +使用`cargo test`命令运行测试: + +``` +$ cargo test + Compiling communicator v0.1.0 (file:///projects/communicator) +error[E0433]: failed to resolve. Use of undeclared type or module `client` + --> src/lib.rs:9:9 + | +9 | client::connect(); + | ^^^^^^^^^^^^^^^ Use of undeclared type or module `client` + +warning: function is never used: `connect`, #[warn(dead_code)] on by default + --> src/network/server.rs:1:1 + | +1 | fn connect() { + | ^ +``` + +编译失败了,不过为什么呢?并不需要像 *src/main.rs* 那样将`communicator::`置于函数前,因为这里肯定是在`communicator`库 crate 之内的。之所以失败的原因是路径是相对于当前模块的,在这里就是`tests`。唯一的例外就是`use`语句,它默认是相对于 crate 根模块的。我们的`tests`模块需要`client`模块位于其作用域中! + +那么如何在模块层次结构中回退一级模块,以便在`tests`模块中能够调用`client::connect`函数呢?在`tests`模块中,要么可以在开头使用双冒号来让 Rust 知道我们想要从根模块开始并列出整个路径: + +```rust,ignore +::client::connect(); +``` + +要么可以使用`super`在层级中获取当前模块的上一级模块: + +```rust,ignore +super::client::connect(); +``` + +在这个例子中这两个选择看不出有多么大的区别,不过随着模块层次的更加深入,每次都从根模块开始就会显得很长了。在这些情况下,使用`super`来获取当前模块的同级模块是一个好的捷径。再加上,如果在代码中的很多地方指定了从根开始的路径,那么当通过移动子树或到其他位置来重新排列模块时,最终就需要更新很多地方的路径,这就非常乏味无趣了。 + +在每一个测试中总是不得不编写`super::`也会显得很恼人,不过你已经见过解决这个问题的利器了:`use`!`super::`的功能改变了提供给`use`的路径,使其不再相对于根模块而是相对于父模块。 + +为此,特别是在`tests`模块,`use super::something`是常用的手段。所以现在的测试看起来像这样: + +Filename: src/lib.rs + +```rust +#[cfg(test)] +mod tests { + use super::client; + + #[test] + fn it_works() { + client::connect(); + } +} +``` + +如果再次运行`cargo test`,测试将会通过而且测试结果输出的第一部分将会是: + +``` +$ cargo test + Compiling communicator v0.1.0 (file:///projects/communicator) + Running target/debug/communicator-92007ddb5330fa5a + +running 1 test +test tests::it_works ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured +``` + +## 总结 + +现在你掌握了组织代码的核心科技!利用他们将相关的代码组合在一起、防止代码文件过长并将一个整洁的公有 API 展现给库的用户。 + +接下来,让我们看看一些标准库提供的集合数据类型,你可以利用他们编写出漂亮整洁的代码。 \ No newline at end of file diff --git a/src/ch08-00-common-collections.md b/src/ch08-00-common-collections.md new file mode 100644 index 0000000..d8fce89 --- /dev/null +++ b/src/ch08-00-common-collections.md @@ -0,0 +1,17 @@ +# 通用集合类型 + +> [ch08-00-common-collections.md](https://github.com/rust-lang/book/blob/master/src/ch08-00-common-collections.md) +>
+> commit 0d229cc5a3da341196e15a6761735b2952281569 + +Rust 标准库中包含一系列被称为**集合**(*collections*)的非常有用的数据结构。大部分其他数据类型都代表一个特定的值,不过集合可以包含多个值。不同于内建的数组和元组类型,这些集合指向的数据是储存在堆上的,这意味着数据的数量不必在编译时就可知并且可以随着程序的运行增长或缩小。每种集合都有着不同能力和代价,而为所处的场景选择合适的集合则是你将要始终发展的技能。在这一章里,我们将详细的了解三个在 Rust 程序中被广泛使用的集合: + +* *vector* 允许我们一个挨着一个地储存一系列数量可变的值 +* **字符串**(*string*)是一个字符的集合。我们之前见过`String`类型,现在将详细介绍它。 +* **哈希 map**(*hash map*)允许我们将值与一个特定的键(key)相关联。这是一个叫做 *map* 的更通用的数据结构的特定实现。 + +对于标准库提供的其他类型的集合,请查看[文档][collections]。 + +[collections]: ../std/collections + +我们将讨论如何创建和更新 vector、字符串和哈希 map,以及他们何以如此特殊。 \ No newline at end of file diff --git a/src/ch08-01-vectors.md b/src/ch08-01-vectors.md new file mode 100644 index 0000000..e69de29 diff --git a/src/ch08-02-strings.md b/src/ch08-02-strings.md new file mode 100644 index 0000000..e69de29 diff --git a/src/ch08-03-hash-maps.md b/src/ch08-03-hash-maps.md new file mode 100644 index 0000000..e69de29