x-i18n: generated_at: “2026-03-01T13:54:57Z” model: gemini-3-flash-preview provider: google-gemini-cli source_hash: 34c07704e03b7706697506a533d9b66f3c256a08a2c6d8001f8f7707a6e919ff source_path: ch07-04-bringing-paths-into-scope-with-the-use-keyword.md workflow: 16
使用 use 关键字将路径引入作用域 (Bringing Paths into Scope with the use Keyword)
Bringing Paths into Scope with the use Keyword
必须写出调用函数的完整路径可能会让人感到不便且重复。在示例 7-7 中,无论我们选择 add_to_waitlist 函数的绝对路径还是相对路径,每次想要调用 add_to_waitlist 时都必须指定 front_of_house 和 hosting。幸运的是,有一种方法可以简化这个过程:我们可以使用 use 关键字为路径创建一个快捷方式,然后在该作用域的其他地方使用较短的名称。
Having to write out the paths to call functions can feel inconvenient and
repetitive. In Listing 7-7, whether we chose the absolute or relative path to
the add_to_waitlist function, every time we wanted to call add_to_waitlist
we had to specify front_of_house and hosting too. Fortunately, there’s a
way to simplify this process: We can create a shortcut to a path with the use
keyword once and then use the shorter name everywhere else in the scope.
在示例 7-11 中,我们将 crate::front_of_house::hosting 模块引入了 eat_at_restaurant 函数的作用域,这样我们只需指定 hosting::add_to_waitlist 即可在 eat_at_restaurant 中调用 add_to_waitlist 函数。
In Listing 7-11, we bring the crate::front_of_house::hosting module into the
scope of the eat_at_restaurant function so that we only have to specify
hosting::add_to_waitlist to call the add_to_waitlist function in
eat_at_restaurant.
{{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-11/src/lib.rs}}
在作用域中添加 use 和路径类似于在文件系统中创建符号链接。通过在 crate 根中添加 use crate::front_of_house::hosting,hosting 现在在该作用域中是一个有效的名称,就像 hosting 模块就是在 crate 根中定义的一样。通过 use 引入作用域的路径也会检查私有性,就像其他任何路径一样。
Adding use and a path in a scope is similar to creating a symbolic link in
the filesystem. By adding use crate::front_of_house::hosting in the crate
root, hosting is now a valid name in that scope, just as though the hosting
module had been defined in the crate root. Paths brought into scope with use
also check privacy, like any other paths.
请注意,use 仅为发生 use 的特定作用域创建快捷方式。示例 7-12 将 eat_at_restaurant 函数移动到一个名为 customer 的新子模块中,这与 use 语句属于不同的作用域,因此函数体将无法编译。
Note that use only creates the shortcut for the particular scope in which the
use occurs. Listing 7-12 moves the eat_at_restaurant function into a new
child module named customer, which is then a different scope than the use
statement, so the function body won’t compile.
{{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-12/src/lib.rs}}
编译器错误显示该快捷方式在 customer 模块内不再适用:
The compiler error shows that the shortcut no longer applies within the
customer module:
{{#include ../listings/ch07-managing-growing-projects/listing-07-12/output.txt}}
请注意,还有一个警告,提示 use 在其作用域内不再被使用!要修复此问题,也将 use 移动到 customer 模块内,或者在子模块 customer 内使用 super::hosting 引用父模块中的快捷方式。
Notice there’s also a warning that the use is no longer used in its scope! To
fix this problem, move the use within the customer module too, or reference
the shortcut in the parent module with super::hosting within the child
customer module.
创建惯用的 use 路径 (Creating Idiomatic use Paths)
Creating Idiomatic use Paths
在示例 7-11 中,你可能会奇怪为什么我们指定 use crate::front_of_house::hosting 然后在 eat_at_restaurant 中调用 hosting::add_to_waitlist ,而不是像示例 7-13 那样一直指定 use 路径到 add_to_waitlist 函数以达到同样的结果。
In Listing 7-11, you might have wondered why we specified use crate::front_of_house::hosting and then called hosting::add_to_waitlist in
eat_at_restaurant, rather than specifying the use path all the way out to
the add_to_waitlist function to achieve the same result, as in Listing 7-13.
{{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-13/src/lib.rs}}
虽然示例 7-11 和示例 7-13 都完成了相同的任务,但示例 7-11 是使用 use 将函数引入作用域的惯用方式。将函数的父模块引入作用域意味着我们在调用函数时必须指定父模块。在调用函数时指定父模块可以清楚地表明该函数不是本地定义的,同时还能最大限度地减少完整路径的重复。示例 7-13 中的代码对于 add_to_waitlist 在何处定义并不清晰。
Although both Listing 7-11 and Listing 7-13 accomplish the same task, Listing
7-11 is the idiomatic way to bring a function into scope with use. Bringing
the function’s parent module into scope with use means we have to specify the
parent module when calling the function. Specifying the parent module when
calling the function makes it clear that the function isn’t locally defined
while still minimizing repetition of the full path. The code in Listing 7-13 is
unclear as to where add_to_waitlist is defined.
另一方面,当使用 use 引入结构体、枚举和其他项时,惯用的做法是指定完整路径。示例 7-14 显示了将标准库的 HashMap 结构体引入二进制 crate 作用域的惯用方式。
On the other hand, when bringing in structs, enums, and other items with use,
it’s idiomatic to specify the full path. Listing 7-14 shows the idiomatic way
to bring the standard library’s HashMap struct into the scope of a binary
crate.
#![allow(unused)]
fn main() {
{{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-14/src/main.rs}}
}
这种惯用法背后并没有强有力的理由:这只是一种已经形成的惯例,人们已经习惯了以这种方式阅读和编写 Rust 代码。
There’s no strong reason behind this idiom: It’s just the convention that has emerged, and folks have gotten used to reading and writing Rust code this way.
这种惯例的例外情况是我们要使用 use 语句将两个同名的项引入作用域,因为 Rust 不允许这样做。示例 7-15 展示了如何将两个同名但父模块不同的 Result 类型引入作用域,以及如何引用它们。
The exception to this idiom is if we’re bringing two items with the same name
into scope with use statements, because Rust doesn’t allow that. Listing 7-15 shows how to bring two Result` types into scope that have the same name but
different parent modules, and how to refer to them.
{{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-15/src/lib.rs:here}}
如你所见,使用父模块可以区分这两种 Result 类型。如果相反我们指定 use std::fmt::Result 和 use std::io::Result,我们将在同一个作用域内拥有两个 Result 类型,Rust 将不知道我们在使用 Result 时是指哪一个。
As you can see, using the parent modules distinguishes the two Result types.
If instead we specified use std::fmt::Result and use std::io::Result, we’d
have two Result types in the same scope, and Rust wouldn’t know which one we
meant when we used Result.
使用 as 关键字提供新名称 (Providing New Names with the as Keyword)
Providing New Names with the as Keyword
对于使用 use 将两个同名类型引入同一作用域的问题,还有另一种解决方案:在路径之后,我们可以指定 as 和该类型的一个新本地名称(或“别名”)。示例 7-16 显示了通过使用 as 重命名两个 Result 类型中的一个来编写示例 7-15 代码的另一种方式。
There’s another solution to the problem of bringing two types of the same name
into the same scope with use: After the path, we can specify as and a new
local name, or alias, for the type. Listing 7-16 shows another way to write
the code in Listing 7-15 by renaming one of the two Result types using as.
{{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-16/src/lib.rs:here}}
在第二个 use 语句中,我们为 std::io::Result 类型选择了新名称 IoResult,这不会与我们也引入作用域的来自 std::fmt 的 Result 冲突。示例 7-15 和示例 7-16 都被认为是惯用的,所以选择权在你!
In the second use statement, we chose the new name IoResult for the
std::io::Result type, which won’t conflict with the Result from std::fmt
that we’ve also brought into scope. Listing 7-15 and Listing 7-16 are
considered idiomatic, so the choice is up to you!
使用 pub use 重导出名称 (Re-exporting Names with pub use)
Re-exporting Names with pub use
当我们使用 use 关键字将名称引入作用域时,该名称在导入它的作用域内是私有的。为了使该作用域之外的代码能够像在那个作用域内定义一样引用该名称,我们可以结合使用 pub 和 use。这种技术被称为“重导出 (re-exporting)”,因为我们不仅将一个项引入作用域,还使该项可供他人引入他们的作用域。
When we bring a name into scope with the use keyword, the name is private to
the scope into which we imported it. To enable code outside that scope to refer
to that name as if it had been defined in that scope, we can combine pub and
use. This technique is called re-exporting because we’re bringing an item
into scope but also making that item available for others to bring into their
scope.
示例 7-17 显示了示例 7-11 中的代码,根模块中的 use 更改为 pub use。
Listing 7-17 shows the code in Listing 7-11 with use in the root module
changed to pub use.
{{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-17/src/lib.rs}}
在此更改之前,外部代码必须通过使用路径 restaurant::front_of_house::hosting::add_to_waitlist() 来调用 add_to_waitlist 函数,这也需要将 front_of_house 模块标记为 pub。现在,由于此 pub use 已从根模块重导出了 hosting 模块,外部代码可以改用路径 restaurant::hosting::add_to_waitlist()。
Before this change, external code would have to call the add_to_waitlist
function by using the path
restaurant::front_of_house::hosting::add_to_waitlist(), which also would have
required the front_of_house module to be marked as pub. Now that this pub use has re-exported the hosting module from the root module, external code
can use the path restaurant::hosting::add_to_waitlist() instead.
当代码的内部结构与调用代码的程序员对该领域的思考方式不同时,重导出非常有用。例如,在这个餐厅比喻中,经营餐厅的人考虑的是“前厅”和“后勤”。但来到餐厅的顾客可能不会用这些术语来考虑餐厅的各个部分。使用 pub use,我们可以用一种结构编写代码,但公开另一种不同的结构。这样做可以使我们的库对于开发库的程序员和调用库的程序员都组织良好。我们将在第 14 章的“公开方便的公有 API”中看到另一个关于 pub use 的例子,以及它如何影响 crate 的文档。
Re-exporting is useful when the internal structure of your code is different
from how programmers calling your code would think about the domain. For
example, in this restaurant metaphor, the people running the restaurant think
about “front of house” and “back of house.” But customers visiting a restaurant
probably won’t think about the parts of the restaurant in those terms. With pub use, we can write our code with one structure but expose a different structure.
Doing so makes our library well organized for programmers working on the library
and programmers calling the library. We’ll look at another example of pub use
and how it affects your crate’s documentation in “Exporting a Convenient Public
API” in Chapter 14.
使用外部包 (Using External Packages)
Using External Packages
在第 2 章中,我们编写了一个猜谜游戏项目,它使用了一个名为 rand 的外部包来获取随机数。为了在我们的项目中使用 rand,我们将此行添加到了 Cargo.toml 中:
In Chapter 2, we programmed a guessing game project that used an external
package called rand to get random numbers. To use rand in our project, we
added this line to Cargo.toml:
{{#include ../listings/ch02-guessing-game-tutorial/listing-02-02/Cargo.toml:9:}}
在 Cargo.toml 中添加 rand 作为依赖项告诉 Cargo 从 crates.io 下载 rand 包及其任何依赖项,并使 rand 对我们的项目可用。
Adding rand as a dependency in Cargo.toml tells Cargo to download the
rand package and any dependencies from crates.io and
make rand available to our project.
然后,为了将 rand 的定义引入我们的包的作用域,我们添加了一行以 crate 名称 rand 开头的 use 语句,并列出了我们想要引入作用域的项。回想一下第 2 章的“生成随机数”中,我们将 Rng 特征引入了作用域并调用了 rand::thread_rng 函数:
Then, to bring rand definitions into the scope of our package, we added a
use line starting with the name of the crate, rand, and listed the items we
wanted to bring into scope. Recall that in “Generating a Random
Number” in Chapter 2, we brought the Rng trait into
scope and called the rand::thread_rng function:
{{#rustdoc_include ../listings/ch02-guessing-game-tutorial/listing-02-03/src/main.rs:ch07-04}}
Rust 社区成员在 crates.io 上提供了许多包,将其中任何一个拉入你的包都涉及这些相同的步骤:在你的包的 Cargo.toml 文件中列出它们,并使用 use 将它们 crate 中的项引入作用域。
Members of the Rust community have made many packages available at
crates.io, and pulling any of them into your package
involves these same steps: listing them in your package’s Cargo.toml file and
using use to bring items from their crates into scope.
请注意,标准 std 库也是一个对我们的包而言是外部的 crate。由于标准库是随 Rust 语言一起提供的,我们不需要更改 Cargo.toml 来包含 std。但我们确实需要用 use 来引用它,以便将其中的项引入我们的包作用域。例如,对于 HashMap,我们会使用这一行:
Note that the standard std library is also a crate that’s external to our
package. Because the standard library is shipped with the Rust language, we
don’t need to change Cargo.toml to include std. But we do need to refer to
it with use to bring items from there into our package’s scope. For example,
with HashMap we would use this line:
#![allow(unused)]
fn main() {
use std::collections::HashMap;
}
这是一个从 std(标准库 crate 的名称)开始的绝对路径。
This is an absolute path starting with std, the name of the standard library
crate.
使用嵌套路径清理大型 use 列表 (Using Nested Paths to Clean Up use Lists)
Using Nested Paths to Clean Up use Lists
如果我们使用定义在同一个 crate 或同一个模块中的多个项,将每个项列在单独的一行中会占用我们文件中大量的垂直空间。例如,我们在示例 2-4 的猜谜游戏中有的这两条 use 语句将 std 中的项引入了作用域:
If we’re using multiple items defined in the same crate or same module, listing
each item on its own line can take up a lot of vertical space in our files. For
example, these two use statements we had in the guessing game in Listing 2-4
bring items from std into scope:
{{#rustdoc_include ../listings/ch07-managing-growing-projects/no-listing-01-use-std-unnested/src/main.rs:here}}
相反,我们可以使用嵌套路径在一行中将相同的项引入作用域。我们通过指定路径的公共部分,后跟两个冒号,然后在花括号中列出路径中不同的部分来实现这一点,如示例 7-18 所示。
Instead, we can use nested paths to bring the same items into scope in one line. We do this by specifying the common part of the path, followed by two colons, and then curly brackets around a list of the parts of the paths that differ, as shown in Listing 7-18.
{{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-18/src/main.rs:here}}
在更大的程序中,使用嵌套路径从同一 crate 或模块引入许多项可以大幅减少所需的单独 use 语句数量!
In bigger programs, bringing many items into scope from the same crate or
module using nested paths can reduce the number of separate use statements
needed by a lot!
我们可以在路径的任何级别使用嵌套路径,这在合并共享子路径的两条 use 语句时非常有用。例如,示例 7-19 显示了两条 use 语句:一条将 std::io 引入作用域,另一条将 std::io::Write 引入作用域。
We can use a nested path at any level in a path, which is useful when combining
two use statements that share a subpath. For example, Listing 7-19 shows two
use statements: one that brings std::io into scope and one that brings
std::io::Write into scope.
{{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-19/src/lib.rs}}
这两条路径的公共部分是 std::io,这正是完整的第一条路径。为了将这两条路径合并为一条 use 语句,我们可以在嵌套路径中使用 self,如示例 7-20 所示。
The common part of these two paths is std::io, and that’s the complete first
path. To merge these two paths into one use statement, we can use self in
the nested path, as shown in Listing 7-20.
{{#rustdoc_include ../listings/ch07-managing-growing-projects/listing-07-20/src/lib.rs}}
这一行将 std::io 和 std::io::Write 引入了作用域。
This line brings std::io and std::io::Write into scope.
使用通配符运算符导入项 (Importing Items with the Glob Operator)
Importing Items with the Glob Operator
如果我们想将路径中定义的所有公有项都引入作用域,我们可以指定该路径,后跟 * 通配符运算符:
If we want to bring all public items defined in a path into scope, we can
specify that path followed by the * glob operator:
#![allow(unused)]
fn main() {
use std::collections::*;
}
这条 use 语句将 std::collections 中定义的所有公有项引入当前作用域。使用通配符运算符时要小心!通配符会使识别哪些名称在作用域内以及程序中使用的名称是在何处定义的变得更加困难。此外,如果依赖项更改了其定义,你导入的内容也会随之更改,这可能会在升级依赖项时导致编译器错误,例如,如果依赖项添加了一个与你在同一作用域内的定义同名的定义。
This use statement brings all public items defined in std::collections into
the current scope. Be careful when using the glob operator! Glob can make it
harder to tell what names are in scope and where a name used in your program
was defined. Additionally, if the dependency changes its definitions, what
you’ve imported changes as well, which may lead to compiler errors when you
upgrade the dependency if the dependency adds a definition with the same name
as a definition of yours in the same scope, for example.
通配符运算符常在测试时使用,用于将所有受测项引入 tests 模块;我们将在第 11 章的“如何编写测试”中讨论。通配符运算符有时也被用作 prelude(预导入)模式的一部分:有关该模式的更多信息,请参阅标准库文档。
The glob operator is often used when testing to bring everything under test into
the tests module; we’ll talk about that in “How to Write
Tests” in Chapter 11. The glob operator is also
sometimes used as part of the prelude pattern: See the standard library
documentation for more
information on that pattern.