地址原文 https://doc.rust-lang.org/edition-guide/rust-2018/module-system/index.html
3.1模块系统
在本指南的这一章中,我们将讨论模块系统的一些变化。 其中最值得注意的是路径清晰度的变化。
3.1.1 原始标识符
与许多编程语言一样,Rust具有“关键字”的概念。 这些标识符对语言有意义,因此您不能在变量名,函数名和其他位置使用它们。 原始标识符允许您使用通常不允许的关键字。
例如,match是一个关键字。 如果您尝试编译此函数:
fn match(needle: &str, haystack: &str) -> bool {
haystack.contains(needle)
}
你会收到这个错误:
error: expected identifier, found keyword `match`
--> src/main.rs:4:4
|
4 | fn match(needle: &str, haystack: &str) -> bool {
| ^^^^^ expected identifier, found keyword
你可以用原始标识符写这个:
fn r#match(needle: &str, haystack: &str) -> bool {
haystack.contains(needle)
}
fn main() {
assert!(r#match("foo", "foobar"));
}
注意函数名称和调用上的r#前缀。
动机
由于一些原因,此功能很有用,但主要动机是版本间的情况。 例如,try不是2015版的关键字,而是2018年版。 因此,如果你有一个用Rust 2015编写并具有try函数的库,要在Rust 2018中调用它,你需要使用原始标识符。
新关键字
2018年版中新确认的关键字是:
async 和 await
这里,async保留用于async fn 以及async || 和 闭包async { … }块。 同时,保留await以保持我们的选项在await!(expr)语法方面是开放的。 有关更多详细信息,请参阅RFC 2394。
try
do catch {…}块已重命名为try {…}并为了支持这一点,关键字try在2018版中保留。有关详细信息,请参阅RFC 2388。
3.1.2 路径清晰度
对于刚接触Rust的人来说,模块系统通常是最困难的事情之一。 当然,每个人都有自己的东西,需要时间来掌握,但有一个根本原因,为什么它对许多人来说如此混乱:虽然有简单而一致的规则来定义模块系统,但它们的后果可能会感觉不一致,违反直觉和神秘。
因此,2018版Rust引入了一些新的模块系统功能,但它们最终简化了模块系统,使其更加清晰。
这是一个简短的总结:
- extern crate 在99%的情况下不再需要外部箱子。
- crate关键字指的是当前的crate。
- 绝对路径以包名称开头,其中关键字crate指的是当前包。
- foo.rs和foo /子目录可以共存; 将子模块放在子目录中时不再需要mod.rs。
这样看起来就像任意新的规则,但心理模型现在整体上大大简化了。 请阅读以获得更多详情!
此外,在nightly版,还有一个额外的可能调整称为“统一路径”的路径。 这与新路径更改向后兼容。 统一路径在本指南末尾有专门的部分。
更多细节
让我们依次讨论每个新功能。
没有更多extern crate
这个非常简单:您不再需要编写extern crate来将crate导入到您的项目中。 之前:
// Rust 2015
extern crate futures;
mod submodule {
use futures::Future;
}
之后:
// Rust 2018
mod submodule {
use futures::Future;
}
现在,要为项目添加新的包,您可以将它添加到您的Cargo.toml,然后没有第二步。 如果你没有使用Cargo,你必须通过–extern标志来提供外部板条箱的位置,所以你只需继续做你在那里做的事情。
这里有一个小小的注意事项:cargo fix 目前不会自动化这一变化。 我们可能会在将来为您做到这一点。
例外
这个规则有一个例外,那就是“sysroot”箱子。 这些是Rust自己分发的包装箱。 我们最终还是想删除对它们的extern crate的要求,但它还没有发货。
你需要使用extern crate来:
- proc_macro
此外,您需要将其用于: - core
- std
但是,extern crate std; 已经隐含了,并使用#![no_std],extern crate core; 已经隐含了。 在高度专业化的情况下,你只需要这些。
最后,在nightly,您将需要它为板条箱,如:
- alloc
- test
Macros
extern crate的另一个用途是导入宏; 那不再需要了。 查看宏部分了解更多信息。
如果你一直用来像这样重命名你的箱子:
extern crate futures as f;
use f::Future;
然后自行删除外部包装箱行将无法正常工作。 你需要这样做:
use futures as f;
use self::f::Future;
此更改将需要在使用f的任何模块中进行。
crate关键字指的是当前的crate。
在使用声明和其他代码中,您可以使用crate ::前缀来引用当前包的根。 例如,crate :: foo :: bar将始终引用模块foo中的名称栏,来自同一个箱子中的任何其他位置。
前缀::以前称为板条箱根或外部板条箱; 它现在毫不含糊地指的是外部箱子。 例如,:: foo :: bar总是引用外部crate foo中的名称栏。
对路径的更改
在Rust 2018中,使用声明中的路径必须以包名,crate,self或super开头。
代码看起来像这样:
// Rust 2015
extern crate futures;
use futures::Future;
mod foo {
pub struct Bar;
}
use foo::Bar;
现在看起来像这样:
// Rust 2018
// 'futures' is the name of a crate
use futures::Future;
mod foo {
pub struct Bar;
}
// 'crate' means the current crate
use crate::foo::Bar;
此外,所有这些路径形式也可以在使用声明之外使用,这消除了许多混淆源。 在Rust 2015中考虑以下代码:
// Rust 2015
extern crate futures;
mod submodule {
// this works!
use futures::Future;
// so why doesn't this work?
fn my_poll() -> futures::Poll { ... }
}
fn main() {
// this works
let five = std::sync::Arc::new(5);
}
mod submodule {
fn function() {
// ... so why doesn't this work
let five = std::sync::Arc::new(5);
}
}
在实际代码中,你不能重复mod子模块,并且函数将在第一个mod块中定义。
在期货示例中,my_poll函数签名不正确,因为子模块不包含名为期货的项目; 也就是说,这条路径被认为是相对的。 使用期货::尽管单独的期货::没有! 随着std它可能会更加令人困惑,因为你从未写过extern crate std; 完全一致。 那么为什么它在main中工作但不在子模块中工作? 同样的事情:它是一个相对路径,因为它不在使用声明中。 extern crate std; 在箱子根处插入,所以它在main中很好,但它根本不存在于子模块中。
让我们来看看这种变化如何影响事物:
// Rust 2018
// no more `extern crate futures;`
mod submodule {
// 'futures' is the name of a crate, so this works
use futures::Future;
// 'futures' is the name of a crate, so this works
fn my_poll<T, E>() -> futures::Poll {
unimplemented!()
}
fn function() {
// 'std' is the name of a crate, so this works
let five = std::sync::Arc::new(5);
}
}
fn main() {
// 'std' is the name of a crate, so this works
let five = std::sync::Arc::new(5);
}
更直截了当。
在Rust 2015中,如果你有一个子模块:
/// foo.rs
/// or
/// foo/mod.rs
mod foo;
它可以存在于foo.rs或foo / mod.rs中。 如果它有自己的子模块,它必须是foo / mod.rs。 因此,foo的bar子模块将存在于foo / bar.rs中。
在Rust 2018中,不再需要mod.rs。
/// foo.rs
/// foo/bar.rs
mod foo;
/// in foo.rs
mod bar;
foo.rs可以只是foo.rs,子模块仍然是foo / bar.rs。 这消除了特殊的名称,如果你在编辑器中打开了一堆文件,你可以清楚地看到他们的名字,而不是有一堆名为mod.rs的标签。
统一的路径
统一路径是夜间特色。
与Rust 2015相比,Rust 2018的统一路径变体简化并统一了路径处理。在Rust 2015中,路径在使用声明中的工作方式与其他地方不同。 特别是,使用声明中的路径总是从包根开始,而其他代码中的路径隐含地从当前模块开始。 这些差异在顶级模块中没有任何影响,这意味着在完成一个足够大的子模块项目之前,所有内容都会显得简单明了。
在Rust 2018的统一路径变体中,使用声明和其他代码中的路径始终以相同的方式工作,无论是在顶级模块还是在任何子模块中。 您始终可以使用当前模块的相对路径,从外部包名称开始的路径,或以crate,super或self开头的路径。
代码看起来像这样:
// Rust 2015
extern crate futures;
use futures::Future;
mod foo {
pub struct Bar;
}
use foo::Bar;
fn my_poll() -> futures::Poll { ... }
enum SomeEnum {
V1(usize),
V2(String),
}
fn func() {
let five = std::sync::Arc::new(5);
use SomeEnum::*;
match ... {
V1(i) => { ... }
V2(s) => { ... }
}
}
在Rust 2018中看起来完全一样,除了你可以删除extern crate行:
// Rust 2018 (uniform paths variant)
use futures::Future;
mod foo {
pub struct Bar;
}
use foo::Bar;
fn my_poll() -> futures::Poll { ... }
enum SomeEnum {
V1(usize),
V2(String),
}
fn func() {
let five = std::sync::Arc::new(5);
use SomeEnum::*;
match ... {
V1(i) => { ... }
V2(s) => { ... }
}
}
但是,使用Rust 2018,相同的代码也可以在子模块中完全不加修改地工作:
// Rust 2018 (uniform paths variant)
mod submodule {
use futures::Future;
mod foo {
pub struct Bar;
}
use foo::Bar;
fn my_poll() -> futures::Poll { ... }
enum SomeEnum {
V1(usize),
V2(String),
}
fn func() {
let five = std::sync::Arc::new(5);
use SomeEnum::*;
match ... {
V1(i) => { ... }
V2(s) => { ... }
}
}
}
这样可以轻松地在项目中移动代码,并避免为多模块项目带来额外的复杂性。
如果路径不明确,例如,如果您有外部包和本地模块或具有相同名称的项目,您将收到错误,并且您需要重命名其中一个冲突名称或明确消除路径歧义。 要明确消除路径歧义,请使用:: name作为外部包名称,或使用self :: name作为本地模块或项目。
3.1.3更多可见性修饰符
您可以使用pub关键字将某些内容作为模块公共接口的一部分。 但此外,还有一些新形式:
pub(crate) struct Foo;
pub(in a::b::c) struct Bar;
第一种形式使Foo结构公开给你的整个箱子,但不是外部的。 第二种形式是类似的,但在另一个模块中使Bar公开,在这种情况下为:: b :: c。
3.1.4嵌套导入使用
最小Rust版本:1.25
Rust:嵌套导入组中添加了一种编写use语句的新方法。 如果您曾编写过这样的一组导入:
#![allow(unused_variables)]
fn main() {
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};
}
你现在可以这样写:
#![allow(unused_variables)]
fn main() {
mod foo {
// on one line
use std::{fs::File, io::Read, path::{Path, PathBuf}};
}
mod bar {
// with some more breathing room
use std::{
fs::File,
io::Read,
path::{
Path,
PathBuf
}
};
}
}
这可以减少一些重复,并使事情更清晰。