Use声明

use-declarations.md
commit: eabdf09207bf3563ae96db9d576de0758c413d5d
本章译文最后维护日期:2021-1-24

句法:
UseDeclaration :
   use UseTree ;

UseTree :
      (SimplePath? ::)? *
   | (SimplePath? ::)? { (UseTree ( , UseTree )* ,?)? }
   | SimplePath ( as ( IDENTIFIER | _ ) )?

use声明用来创建一个或多个与程序项路径同义的本地名称绑定。通常使用 use声明来缩短引用模块所需的路径。这些声明可以出现在模块中,但通常在作用域顶部。

use声明支持多种便捷方法:

  • 使用带有花括号的 glob-like(::) 句法 use a::b::{c, d, e::f, g::h::i}; 来同时绑定一个系列有共同前缀的路径。
  • 使用关键字 self,例如 use a::b::{self, c, d::e};,来同时绑定一系列有共同前缀和共同父模块的路径。
  • 使用句法 use p::q::r as x; 将编译目标名称重新绑定为新的本地名称。这种也可以和上面两种方法一起使用:use a::b::{self as ab, c as abc}
  • 使用星号通配符句法 use a::b::*; 来绑定与给定前缀匹配的所有路径。
  • 将前面的方法嵌套重复使用,例如 use a::b::{self as ab, c, d::{*, e::f}};

use声明的一个示例:

use std::option::Option::{Some, None};
use std::collections::hash_map::{self, HashMap};

fn foo<T>(_: T){}
fn bar(map1: HashMap<String, usize>, map2: hash_map::HashMap<String, usize>){}

fn main() {
    // 等价于 'foo(vec![std::option::Option::Some(1.0f64), std::option::Option::None]);'
    foo(vec![Some(1.0f64), None]);

    // `hash_map` 和 `HashMap` 在当前作用域内都有效.
    let map1 = HashMap::new();
    let map2 = hash_map::HashMap::new();
    bar(map1, map2);
}

use可见性

与其他程序项一样,默认情况下,use声明对包含它的模块来说是私有的。同样的,如果使用关键字 pub 进行限定,use声明也可以是公有的。use声明可用于*重导出(re-expor)*名称。因此,公有的 use声明可以将某些公有名称重定向到不同的目标定义中:甚至是位于不同模块内具有私有可见性的规范路径定义中。如果这样的重定向序列形成一个循环或不能明确地解析,则会导致编译期错误。

重导出的一个示例:

mod quux {
    pub use self::foo::{bar, baz};
    pub mod foo {
        pub fn bar() {}
        pub fn baz() {}
    }
}

fn main() {
    quux::bar();
    quux::baz();
}

在本例中,模块 quux 重导出了在模块 foo 中定义的两个公共名称。

use 路径

注意:本章节内容还不完整。

一些正常和不正常的使用 use程序项的例子:

#![allow(unused_imports)]
use std::path::{self, Path, PathBuf};  // good: std 是一个 crate 名称
use crate::foo::baz::foobaz;    // good: foo 在当前 crate 的第一层

mod foo {

    pub mod example {
        pub mod iter {}
    }

    use crate::foo::example::iter; // good: foo 在当前 crate 的第一层
//  use example::iter;      // 在 2015 版里不行,2015 版里相对路径必须以 `self` 开头; 2018 版这样写没问题
    use self::baz::foobaz;  // good: `self` 指的是 'foo' 模块
    use crate::foo::bar::foobar;   // good: foo 在当前 crate 的第一层

    pub mod bar {
        pub fn foobar() { }
    }

    pub mod baz {
        use super::bar::foobar; // good: `super` 指的是 'foo' 模块
        pub fn foobaz() { }
    }
}

fn main() {}

版本差异: 在 2015 版中,use路径也允许访问 crate 根模块中的程序项。继续使用上面的例子,那以下 use路径的用法在 2015 版中有效,在 2018 版中就无效了:

mod foo {
    pub mod example { pub mod iter {} }
    pub mod baz { pub fn foobaz() {} }
}
use foo::example::iter;
use ::foo::baz::foobaz;
fn main() {}

2015 版不允许用 use声明来引用外部预导入包里的 crate。因此,在2015 版中仍然需要使用 extern crate声明,以便在 use声明中去引用外部 crate。从 2018 版开始,use声明可以像 extern crate 一样指定外部 crate 依赖关系。

在 2018 版中,如果本地程序项与外部的 crate 名称相同,那么使用该 crate 名称需要一个前导的 :: 来明确地选择该 crate 名称。这种做法是为了与未来可能发生的更改保持兼容。

// use std::fs; // 错误, 这样有歧义.
use ::std::fs;  // 从`std` crate 里导入, 不是下面这个 mod.
use self::std::fs as self_fs;  // 从下面这个 mod 导入.

mod std {
    pub mod fs {}
}
fn main() {}

下划线导入

通过使用形如为 use path as _ 的带下划线的 use声明,可以在不绑定名称的情况下导入程序项。这对于导入一个 trait 特别有用,这样就可以在不导入 trait 的 symbol 的情况下使用这个 trait 的方法,例如,如果 trait 的 symbol 可能与另一个 symbol 冲突。再一个例子是链接外部的 crate 而不导入其名称。

使用星号全局导入(Asterisk glob imports,即 ::*)句法将以 _ 的形式导入能匹配到的所有程序项,但这些程序项在当前作用域中将处于不可命名的状态。

mod foo {
    pub trait Zoo {
        fn zoo(&self) {}
    }

    impl<T> Zoo for T {}
}

use self::foo::Zoo as _;
struct Zoo;  // 下划线形式的导入就是为了避免和这个程序项在名字上起冲突

fn main() {
    let z = Zoo;
    z.zoo();
}

在宏扩展之后会创建一些惟一的、不可命名的 symbols,这样宏就可以安全地扩展出(emit)对 _ 导入的多个引用。例如,下面代码不应该产生错误:


#![allow(unused)]
fn main() {
macro_rules! m {
    ($item: item) => { $item $item }
}

m!(use std as _;);
// 这会扩展出:
// use std as _;
// use std as _;
}