使用use
关键词
必须写出调用函数的路径可能会让人感觉很不方便,并且
重复。在示例 7-7 中,无论我们选择 absolute 还是 relative 路径
这add_to_waitlist
函数,每次我们想调用add_to_waitlist
我们必须指定front_of_house
和hosting
太。幸运的是,有一个
简化此过程的方法:我们可以使用use
keyword 一次,然后在范围中的其他所有位置使用较短的名称。
在示例 7-11 中,我们引入了crate::front_of_house::hosting
模块到
范围eat_at_restaurant
函数,因此我们只需要指定hosting::add_to_waitlist
调用add_to_waitlist
函数eat_at_restaurant
.
文件名: src/lib.rs
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
示例 7-11:使用use
添加use
范围中的路径类似于在
文件系统。加入use crate::front_of_house::hosting
在板条箱中
根hosting
现在是该作用域中的有效名称,就像hosting
module 已在 crate 根中定义。使用use
还要检查隐私,就像任何其他路径一样。
请注意,use
only 为特定范围创建快捷方式,其中use
发生。示例 7-12 将eat_at_restaurant
函数转换为新的
名为customer
,则其范围与use
语句,因此函数体不会编译。
文件名: src/lib.rs
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
use crate::front_of_house::hosting;
mod customer {
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
}
示例 7-12:一个use
语句仅适用于范围
它在
编译器错误显示该快捷方式不再适用于customer
模块:
$ cargo build
Compiling restaurant v0.1.0 (file:///projects/restaurant)
error[E0433]: failed to resolve: use of undeclared crate or module `hosting`
--> src/lib.rs:11:9
|
11 | hosting::add_to_waitlist();
| ^^^^^^^ use of undeclared crate or module `hosting`
|
help: consider importing this module through its public re-export
|
10 + use crate::hosting;
|
warning: unused import: `crate::front_of_house::hosting`
--> src/lib.rs:7:5
|
7 | use crate::front_of_house::hosting;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(unused_imports)]` on by default
For more information about this error, try `rustc --explain E0433`.
warning: `restaurant` (lib) generated 1 warning
error: could not compile `restaurant` (lib) due to 1 previous error; 1 warning emitted
请注意,还有一个警告,指出use
不再在其范围内使用!自
修复此问题,将use
在customer
module too 或 reference
父模块中的快捷方式具有super::hosting
在子级customer
模块。
创建 Idiomaticuse
路径
在示例 7-11 中,你可能想知道为什么我们指定了use crate::front_of_house::hosting
然后调用hosting::add_to_waitlist
在eat_at_restaurant
,而不是指定use
path 一直到
这add_to_waitlist
函数来实现相同的结果,如图 7-13 所示。
文件名: src/lib.rs
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
use crate::front_of_house::hosting::add_to_waitlist;
pub fn eat_at_restaurant() {
add_to_waitlist();
}
示例 7-13:将add_to_waitlist
功能
into 范围use
,这是不地道的
尽管示例 7-11 和示例 7-13 都完成了相同的任务,但 清单
7-11 是将函数引入作用域的惯用方式use
.带
函数的父模块放入作用域中use
意味着我们必须指定
parent 模块。指定父模块
调用该函数可以清楚地表明该函数不是本地定义的
同时仍尽量减少完整路径的重复。示例 7-13 中的代码是
不清楚在哪里add_to_waitlist
已定义。
另一方面,当使用use
,
指定完整路径是惯用的。示例 7-14 显示了惯用的方式
要将标准库的HashMap
struct 添加到二进制文件的作用域中
板条箱。
文件名: src/main.rs
use std::collections::HashMap; fn main() { let mut map = HashMap::new(); map.insert(1, 2); }
示例 7-14:引入HashMap
into 范围内的
惯用方式
这个成语背后没有充分的理由:只是约定俗成 出现,人们已经习惯了以这种方式读取和编写 Rust 代码。
这个习惯用法的例外是,如果我们带来两个同名的项目
into 范围use
语句,因为 Rust 不允许这样做。示例 7-15
演示如何引入两个Result
类型输入到具有相同名称但
不同的父模块,以及如何引用它们。
文件名: src/lib.rs
use std::fmt;
use std::io;
fn function1() -> fmt::Result {
// --snip--
Ok(())
}
fn function2() -> io::Result<()> {
// --snip--
Ok(())
}
示例 7-15:将两个同名的类型引入 相同的范围需要使用它们的父模块。
如您所见,使用父模块可以区分两者Result
类型。
如果我们指定了use std::fmt::Result
和use std::io::Result
,我们会
有两个Result
类型,并且 Rust 不知道我们是哪一个
意思是当我们使用Result
.
使用as
关键词
对于引入两个同名类型的问题,还有另一种解决方案
放入同一范围use
:在 path 之后,我们可以指定as
和一个新的
类型的本地名称或别名。示例 7-16 显示了另一种编写方式
示例 7-15 中的代码,重命名两个Result
types 使用as
.
文件名: src/lib.rs
use std::fmt::Result;
use std::io::Result as IoResult;
fn function1() -> Result {
// --snip--
Ok(())
}
fn function2() -> IoResult<()> {
// --snip--
Ok(())
}
示例 7-16:将类型引入
scope 替换为as
关键词
在第二个use
语句中,我们选择了新名称IoResult
对于std::io::Result
type 的Result
从std::fmt
我们也已将其纳入范围。示例 7-15 和示例 7-16 是
被认为是惯用的,所以选择权在你!
重新导出名称pub use
当我们使用use
keyword,则
新范围是 private。启用调用我们的代码的代码以引用
该名称,就好像它是在该代码的范围内定义的一样,我们可以将pub
和use
.这种技术称为重新导出,因为我们引入了
item 引入范围,但也使该 item 可供其他人引入
他们的范围。
示例 7-17 显示了示例 7-11 中的代码,其中use
在根模块
更改为pub use
.
文件名: src/lib.rs
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
pub use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
示例 7-17:为任何代码提供名称
从新范围pub use
在此更改之前,外部代码必须调用add_to_waitlist
函数restaurant::front_of_house::hosting::add_to_waitlist()
,这也将具有
required 的front_of_house
模块标记为pub
.现在这个pub use
已重新导出hosting
module 从根模块、外部代码
可以使用路径restaurant::hosting::add_to_waitlist()
相反。
当代码的内部结构不同时,重新导出非常有用
从调用代码的程序员如何考虑域。为
例如,在这个餐厅的比喻中,经营餐厅的人认为
关于“Front of House”和“Back of House”。但是顾客光顾餐厅
可能不会从这些方面考虑餐厅的各个部分。跟pub use
,我们可以用一种结构编写代码,但公开不同的
结构。这样做可以使我们的库为程序员提供良好的组织性
库和调用库的程序员。我们将看另一个示例
之pub use
以及它如何影响 crate 的文档。“导出
方便的公共 APIpub use
”部分
第 14 章.
使用外部软件包
在第 2 章中,我们编写了一个猜谜游戏项目,它使用外部
名为rand
获取随机数。要使用rand
在我们的项目中,我们
在 Cargo.toml 中添加了这一行:
文件名: Cargo.toml
rand = "0.8.5"
添加rand
作为 Cargo.toml 中的依赖项告诉 Cargo 下载rand
package 和任何依赖项 crates.io 和
做rand
可用于我们的项目。
然后,为了带来rand
definitions 添加到我们的包的范围内,我们添加了一个use
以 crate 名称开头的行,rand
,并列出了项目
我们想把它们纳入范围。回想一下,在“生成随机
Number“部分,我们引入了Rng
特性
into 范围中,并调用rand::thread_rng
功能:
use std::io;
use rand::Rng;
fn main() {
println!("Guess the number!");
let secret_number = rand::thread_rng().gen_range(1..=100);
println!("The secret number is: {secret_number}");
println!("Please input your guess.");
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line");
println!("You guessed: {guess}");
}
Rust 社区的成员在 crates.io 上提供了许多包,并将其中任何一个拉入您的包中
涉及这些相同的步骤:在软件包的 Cargo.toml 文件中列出它们,并且
用use
将 crate 中的项目带入范围。
请注意,标准std
库也是一个 crate,它位于
包。因为标准库是随 Rust 语言一起提供的,所以我们
不需要更改 Cargo.toml 来包含std
.但我们确实需要参考
它与use
将 Item 从 API 导入到我们的 Package 范围内。例如
跟HashMap
我们将使用这一行:
#![allow(unused)] fn main() { use std::collections::HashMap; }
这是一条绝对路径,从std
、标准库的名称
板条箱。
使用嵌套路径清理大型use
列表
如果我们使用在同一个 crate 或同一个模块中定义的多个项目,请列出
每个项目在自己的行中都会占用大量的垂直空间。为
示例,这两个use
我们在示例 2-4 中的猜谜游戏中的陈述
将物品带自std
into 范围:
文件名: src/main.rs
use rand::Rng;
// --snip--
use std::cmp::Ordering;
use std::io;
// --snip--
fn main() {
println!("Guess the number!");
let secret_number = rand::thread_rng().gen_range(1..=100);
println!("The secret number is: {secret_number}");
println!("Please input your guess.");
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line");
println!("You guessed: {guess}");
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => println!("You win!"),
}
}
相反,我们可以使用嵌套路径将相同的项目合并到一个 线。为此,我们指定了路径的公共部分,后跟两个 冒号,然后在路径的 differ,如示例 7-18 所示。
文件名: src/main.rs
use rand::Rng;
// --snip--
use std::{cmp::Ordering, io};
// --snip--
fn main() {
println!("Guess the number!");
let secret_number = rand::thread_rng().gen_range(1..=100);
println!("The secret number is: {secret_number}");
println!("Please input your guess.");
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line");
let guess: u32 = guess.trim().parse().expect("Please type a number!");
println!("You guessed: {guess}");
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => println!("You win!"),
}
}
示例 7-18:指定一个嵌套路径以引入多个 具有相同前缀的项引入范围
在较大的程序中,将多个项目从同一个 crate 或
模块使用嵌套路径可以减少分隔路径的数量use
语句
很多人都需要!
我们可以在路径中的任何级别使用嵌套路径,这在组合时很有用
二use
共享子路径的语句。例如,示例 7-19 显示了两个use
声明:带来std::io
进入范围,并将std::io::Write
into 范围。
文件名: src/lib.rs
use std::io;
use std::io::Write;
示例 7-19:两个use
语句,其中 one 是子路径
另一个
这两条路径的共同点是std::io
,这是完整的第一个
路径。将这两个路径合并为一个use
语句,我们可以使用self
在
嵌套路径,如示例 7-20 所示。
文件名: src/lib.rs
use std::io::{self, Write};
示例 7-20:将示例 7-19 中的路径合并为
一use
陈述
这条线带来了std::io
和std::io::Write
into 范围。
通配符运算符
如果我们想将 path 中定义的所有公共项都引入 scope,我们可以
指定 glob 运算符后跟的 path:*
#![allow(unused)] fn main() { use std::collections::*; }
这use
语句将std::collections
到
当前范围。使用 glob 运算符时要小心!Glob 可以做到
更难分辨哪些名称在范围内以及名称在程序中的使用位置
被定义。
测试时经常使用 glob 运算符来测试所有内容
到tests
模块;我们将在“How to Write
Tests“部分。glob 运算符
有时也用作 Prelude 样式的一部分:请参阅标准
library 文档,了解有关该模式的更多信息。
本文档由官方文档翻译而来,如有差异请以官方英文文档(https://doc.rust-lang.org/)为准