trpl-zh-cn/docs/ch07-03-importing-names-with-use.html
yang yue 8fcc7696c0 wip
2017-02-26 15:11:54 +08:00

291 lines
16 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>使用`use`导入命名 - Rust 程序设计语言 简体中文版</title>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<meta name="description" content="Rust 程序设计语言 简体中文版">
<meta name="viewport" content="width=device-width, initial-scale=1">
<base href="">
<link rel="stylesheet" href="book.css">
<link href='https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800' rel='stylesheet' type='text/css'>
<link rel="shortcut icon" href="favicon.png">
<!-- Font Awesome -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
<link rel="stylesheet" href="highlight.css">
<link rel="stylesheet" href="tomorrow-night.css">
<!-- MathJax -->
<script type="text/javascript" src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<!-- Fetch JQuery from CDN but have a local fallback -->
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script>
if (typeof jQuery == 'undefined') {
document.write(unescape("%3Cscript src='jquery.js'%3E%3C/script%3E"));
}
</script>
</head>
<body class="light">
<!-- Set the theme before any content is loaded, prevents flash -->
<script type="text/javascript">
var theme = localStorage.getItem('theme');
if (theme == null) { theme = 'light'; }
$('body').removeClass().addClass(theme);
</script>
<!-- Hide / unhide sidebar before it is displayed -->
<script type="text/javascript">
var sidebar = localStorage.getItem('sidebar');
if (sidebar === "hidden") { $("html").addClass("sidebar-hidden") }
else if (sidebar === "visible") { $("html").addClass("sidebar-visible") }
</script>
<div id="sidebar" class="sidebar">
<ul class="chapter"><li><a href="ch01-00-introduction.html"><strong>1.</strong> 介绍</a></li><li><ul class="section"><li><a href="ch01-01-installation.html"><strong>1.1.</strong> 安装</a></li><li><a href="ch01-02-hello-world.html"><strong>1.2.</strong> Hello, World!</a></li></ul></li><li><a href="ch02-00-guessing-game-tutorial.html"><strong>2.</strong> 猜猜看教程</a></li><li><a href="ch03-00-common-programming-concepts.html"><strong>3.</strong> 通用编程概念</a></li><li><ul class="section"><li><a href="ch03-01-variables-and-mutability.html"><strong>3.1.</strong> 变量和可变性</a></li><li><a href="ch03-02-data-types.html"><strong>3.2.</strong> 数据类型</a></li><li><a href="ch03-03-how-functions-work.html"><strong>3.3.</strong> 函数如何工作</a></li><li><a href="ch03-04-comments.html"><strong>3.4.</strong> 注释</a></li><li><a href="ch03-05-control-flow.html"><strong>3.5.</strong> 控制流</a></li></ul></li><li><a href="ch04-00-understanding-ownership.html"><strong>4.</strong> 认识所有权</a></li><li><ul class="section"><li><a href="ch04-01-what-is-ownership.html"><strong>4.1.</strong> 什么是所有权</a></li><li><a href="ch04-02-references-and-borrowing.html"><strong>4.2.</strong> 引用 &amp; 借用</a></li><li><a href="ch04-03-slices.html"><strong>4.3.</strong> Slices</a></li></ul></li><li><a href="ch05-00-structs.html"><strong>5.</strong> 结构体</a></li><li><ul class="section"><li><a href="ch05-01-method-syntax.html"><strong>5.1.</strong> 方法语法</a></li></ul></li><li><a href="ch06-00-enums.html"><strong>6.</strong> 枚举和模式匹配</a></li><li><ul class="section"><li><a href="ch06-01-defining-an-enum.html"><strong>6.1.</strong> 定义枚举</a></li><li><a href="ch06-02-match.html"><strong>6.2.</strong> <code>match</code>控制流运算符</a></li><li><a href="ch06-03-if-let.html"><strong>6.3.</strong> <code>if let</code>简单控制流</a></li></ul></li><li><a href="ch07-00-modules.html"><strong>7.</strong> 模块</a></li><li><ul class="section"><li><a href="ch07-01-mod-and-the-filesystem.html"><strong>7.1.</strong> <code>mod</code>和文件系统</a></li><li><a href="ch07-02-controlling-visibility-with-pub.html"><strong>7.2.</strong> 使用<code>pub</code>控制可见性</a></li><li><a href="ch07-03-importing-names-with-use.html" class="active"><strong>7.3.</strong> 使用<code>use</code>导入命名</a></li></ul></li><li><a href="ch08-00-common-collections.html"><strong>8.</strong> 通用集合类型</a></li><li><ul class="section"><li><a href="ch08-01-vectors.html"><strong>8.1.</strong> vector</a></li><li><a href="ch08-02-strings.html"><strong>8.2.</strong> 字符串</a></li><li><a href="ch08-03-hash-maps.html"><strong>8.3.</strong> 哈希 map</a></li></ul></li><li><a href="ch09-00-error-handling.html"><strong>9.</strong> 错误处理</a></li><li><ul class="section"><li><a href="ch09-01-unrecoverable-errors-with-panic.html"><strong>9.1.</strong> <code>panic!</code>与不可恢复的错误</a></li><li><a href="ch09-02-recoverable-errors-with-result.html"><strong>9.2.</strong> <code>Result</code>与可恢复的错误</a></li><li><a href="ch09-03-to-panic-or-not-to-panic.html"><strong>9.3.</strong> <code>panic!</code>还是不<code>panic!</code></a></li></ul></li><li><a href="ch10-00-generics.html"><strong>10.</strong> 泛型、trait 和生命周期</a></li><li><ul class="section"><li><a href="ch10-01-syntax.html"><strong>10.1.</strong> 泛型数据类型</a></li><li><a href="ch10-02-traits.html"><strong>10.2.</strong> trait定义共享的行为</a></li><li><a href="ch10-03-lifetime-syntax.html"><strong>10.3.</strong> 生命周期与引用有效性</a></li></ul></li></ul>
</div>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar" class="menu-bar">
<div class="left-buttons">
<i id="sidebar-toggle" class="fa fa-bars"></i>
<i id="theme-toggle" class="fa fa-paint-brush"></i>
</div>
<h1 class="menu-title">Rust 程序设计语言 简体中文版</h1>
<div class="right-buttons">
<i id="print-button" class="fa fa-print" title="Print this book"></i>
</div>
</div>
<div id="content" class="content">
<a class="header" href="#导入命名" name="导入命名"><h2>导入命名</h2></a>
<blockquote>
<p><a href="https://github.com/rust-lang/book/blob/master/src/ch07-03-importing-names-with-use.md">ch07-03-importing-names-with-use.md</a>
<br>
commit e2a129961ae346f726f8b342455ec2255cdfed68</p>
</blockquote>
<p>我们已经讲到了如何使用模块名称作为调用的一部分,来调用模块中的函数,如列表 7-6 中所示的<code>nested_modules</code>函数调用。</p>
<figure>
<span class="filename">Filename: src/main.rs</span>
<pre><code class="language-rust">pub mod a {
pub mod series {
pub mod of {
pub fn nested_modules() {}
}
}
}
fn main() {
a::series::of::nested_modules();
}
</code></pre>
<figcaption>
<p>Listing 7-6: Calling a function by fully specifying its enclosing modules
namespaces</p>
</figcaption>
</figure>
<p>如你所见,指定函数的完全限定名称可能会非常冗长。所幸 Rust 有一个关键字使得这些调用显得更简洁。</p>
<a class="header" href="#使用use的简单导入" name="使用use的简单导入"><h3>使用<code>use</code>的简单导入</h3></a>
<p>Rust 的<code>use</code>关键字的工作是缩短冗长的函数调用,通过将想要调用的函数所在的模块引入到作用域中。这是一个将<code>a::series::of</code>模块导入一个二进制 crate 的根作用域的例子:</p>
<p><span class="filename">Filename: src/main.rs</span></p>
<pre><code class="language-rust">pub mod a {
pub mod series {
pub mod of {
pub fn nested_modules() {}
}
}
}
use a::series::of;
fn main() {
of::nested_modules();
}
</code></pre>
<p><code>use a::series::of;</code>这一行的意思是每当想要引用<code>of</code>模块时,不用使用完整的<code>a::series::of</code>路径,可以直接使用<code>of</code></p>
<p><code>use</code>关键字只将指定的模块引入作用域;它并不会将其子模块也引入。这就是为什么想要调用<code>nested_modules</code>函数时仍然必须写成<code>of::nested_modules</code></p>
<p>也可以将函数本身引入到作用域中,通过如下在<code>use</code>中指定函数的方式:</p>
<pre><code class="language-rust">pub mod a {
pub mod series {
pub mod of {
pub fn nested_modules() {}
}
}
}
use a::series::of::nested_modules;
fn main() {
nested_modules();
}
</code></pre>
<p>这使得我们可以忽略所有的模块并直接引用函数。</p>
<p>因为枚举也像模块一样组成了某种命名空间,也可以使用<code>use</code>来导入枚举的成员。对于任何类型的<code>use</code>语句,如果从一个命名空间导入多个项,可以使用大括号和逗号来列举他们,像这样:</p>
<pre><code class="language-rust">enum TrafficLight {
Red,
Yellow,
Green,
}
use TrafficLight::{Red, Yellow};
fn main() {
let red = Red;
let yellow = Yellow;
let green = TrafficLight::Green; // because we didnt `use` TrafficLight::Green
}
</code></pre>
<a class="header" href="#使用的全局引用导入" name="使用的全局引用导入"><h3>使用<code>*</code>的全局引用导入</h3></a>
<p>为了一次导入某个命名空间的所有项,可以使用<code>*</code>语法。例如:</p>
<pre><code class="language-rust">enum TrafficLight {
Red,
Yellow,
Green,
}
use TrafficLight::*;
fn main() {
let red = Red;
let yellow = Yellow;
let green = Green;
}
</code></pre>
<p><code>*</code>被称为<strong>全局导入</strong><em>glob</em>),它会导入命名空间中所有可见的项。全局导入应该保守的使用:他们是方便的,但是也可能会引入多于你预期的内容从而导致命名冲突。</p>
<a class="header" href="#使用super访问父模块" name="使用super访问父模块"><h3>使用<code>super</code>访问父模块</h3></a>
<p>正如我们已经知道的,当创建一个库 crate 时Cargo 会生成一个<code>tests</code>模块。现在让我们来深入了解一下。在<code>communicator</code>项目中,打开 <em>src/lib.rs</em></p>
<p><span class="filename">Filename: src/lib.rs</span></p>
<pre><code class="language-rust,ignore">pub mod client;
pub mod network;
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
}
}
</code></pre>
<p>第十二章会更详细的解释测试,不过其部分内容现在应该可以理解了:有一个叫做<code>tests</code>的模块紧邻其他模块,同时包含一个叫做<code>it_works</code>的函数。即便存在一些特殊注解,<code>tests</code>也不过是另外一个模块!所以我们的模块层次结构看起来像这样:</p>
<pre><code>communicator
├── client
├── network
| └── client
└── tests
</code></pre>
<p>测试是为了检验库中的代码而存在的,所以让我们尝试在<code>it_works</code>函数中调用<code>client::connect</code>函数,即便现在不准备测试任何功能:</p>
<p><span class="filename">Filename: src/lib.rs</span></p>
<pre><code class="language-rust">#[cfg(test)]
mod tests {
#[test]
fn it_works() {
client::connect();
}
}
</code></pre>
<p>使用<code>cargo test</code>命令运行测试:</p>
<pre><code>$ cargo test
Compiling communicator v0.1.0 (file:///projects/communicator)
error[E0433]: failed to resolve. Use of undeclared type or module `client`
--&gt; 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
--&gt; src/network/server.rs:1:1
|
1 | fn connect() {
| ^
</code></pre>
<p>编译失败了,不过为什么呢?并不需要像 <em>src/main.rs</em> 那样将<code>communicator::</code>置于函数前,因为这里肯定是在<code>communicator</code>库 crate 之内的。之所以失败的原因是路径是相对于当前模块的,在这里就是<code>tests</code>。唯一的例外就是<code>use</code>语句,它默认是相对于 crate 根模块的。我们的<code>tests</code>模块需要<code>client</code>模块位于其作用域中!</p>
<p>那么如何在模块层次结构中回退一级模块,以便在<code>tests</code>模块中能够调用<code>client::connect</code>函数呢?在<code>tests</code>模块中,要么可以在开头使用双冒号来让 Rust 知道我们想要从根模块开始并列出整个路径:</p>
<pre><code class="language-rust,ignore">::client::connect();
</code></pre>
<p>要么可以使用<code>super</code>在层级中获取当前模块的上一级模块:</p>
<pre><code class="language-rust,ignore">super::client::connect();
</code></pre>
<p>在这个例子中这两个选择看不出有多么大的区别,不过随着模块层次的更加深入,每次都从根模块开始就会显得很长了。在这些情况下,使用<code>super</code>来获取当前模块的同级模块是一个好的捷径。再加上,如果在代码中的很多地方指定了从根开始的路径,那么当通过移动子树或到其他位置来重新排列模块时,最终就需要更新很多地方的路径,这就非常乏味无趣了。</p>
<p>在每一个测试中总是不得不编写<code>super::</code>也会显得很恼人,不过你已经见过解决这个问题的利器了:<code>use</code><code>super::</code>的功能改变了提供给<code>use</code>的路径,使其不再相对于根模块而是相对于父模块。</p>
<p>为此,特别是在<code>tests</code>模块,<code>use super::something</code>是常用的手段。所以现在的测试看起来像这样:</p>
<p><span class="filename">Filename: src/lib.rs</span></p>
<pre><code class="language-rust">#[cfg(test)]
mod tests {
use super::client;
#[test]
fn it_works() {
client::connect();
}
}
</code></pre>
<p>如果再次运行<code>cargo test</code>,测试将会通过而且测试结果输出的第一部分将会是:</p>
<pre><code>$ 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
</code></pre>
<a class="header" href="#总结" name="总结"><h2>总结</h2></a>
<p>现在你掌握了组织代码的核心科技!利用他们将相关的代码组合在一起、防止代码文件过长并将一个整洁的公有 API 展现给库的用户。</p>
<p>接下来,让我们看看一些标准库提供的集合数据类型,你可以利用他们编写出漂亮整洁的代码。</p>
</div>
<!-- Mobile navigation buttons -->
<a href="ch07-02-controlling-visibility-with-pub.html" class="mobile-nav-chapters previous">
<i class="fa fa-angle-left"></i>
</a>
<a href="ch08-00-common-collections.html" class="mobile-nav-chapters next">
<i class="fa fa-angle-right"></i>
</a>
</div>
<a href="ch07-02-controlling-visibility-with-pub.html" class="nav-chapters previous" title="You can navigate through the chapters using the arrow keys">
<i class="fa fa-angle-left"></i>
</a>
<a href="ch08-00-common-collections.html" class="nav-chapters next" title="You can navigate through the chapters using the arrow keys">
<i class="fa fa-angle-right"></i>
</a>
</div>
<!-- Local fallback for Font Awesome -->
<script>
if ($(".fa").css("font-family") !== "FontAwesome") {
$('<link rel="stylesheet" type="text/css" href="_FontAwesome/css/font-awesome.css">').prependTo('head');
}
</script>
<!-- Livereload script (if served using the cli tool) -->
<script src="highlight.js"></script>
<script src="book.js"></script>
</body>
</html>