edition-guide-2018 模块系统(译)

地址原文 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);
}

更直截了当。

没有更多的mod.rs

在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
    }
};
}
}

这可以减少一些重复,并使事情更清晰。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值