Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

名称解析

_名称解析_是将路径和其他标识符绑定到这些实体的声明的过程。名称被分成不同的命名空间,允许不同命名空间中的实体共享相同的名称而不冲突。每个名称在作用域内有效,即可以引用该名称的源文本区域。对名称的访问可能会根据其可见性受到限制。

名称解析在整个编译过程中分为三个阶段。第一阶段,展开时解析,解析所有 [use 声明]和宏调用。第二阶段,主要解析,解析所有尚未解析且不依赖于类型信息来解析的名称。最后阶段,类型相关解析,一旦类型信息可用,就解析剩余的名称。

Note

展开时解析也称为早期解析。主要解析也称为晚期解析

通用

本节中的规则适用于名称解析的所有阶段。

作用域

Note

这是关于各种作用域内名称解析的未来扩展的占位符。

展开时名称解析

展开时名称解析是完成宏展开并完全生成 crate 的 AST 所需的名称解析阶段。此阶段需要解析宏调用和 use 声明。解析 use 声明是通过基于路径的作用域解析的宏调用所必需的。解析宏调用是为了展开它们。

展开时名称解析后,AST 不得包含任何未展开的宏调用。每个宏调用都解析为最终 AST 或外部 crate 中存在的有效定义。

#![allow(unused)]
fn main() {
m!(); // ERROR: Cannot find macro `m` in this scope.
}

名称的解析必须是稳定的。展开后,完全展开的 AST 中的名称必须解析到相同的定义,无论宏展开和导入解析的顺序如何。

在宏展开期间选择的所有名称解析候选都被视为投机性的。一旦 crate 完全展开,所有投机性导入解析都会被验证,以确保宏展开没有引入任何新的歧义。

Note

由于宏展开的迭代性质,这会导致所谓的时空旅行歧义,例如当宏或 glob 导入引入一个与其自身基本路径有歧义的项时。

fn main() {}
macro_rules! f {
    () => {
        mod m {
            pub(crate) use f;
        }
    }
}
f!();

const _: () = {
    // Initially, we speculatively resolve `m` to the module in
    // the crate root.
    //
    // Expansion of `f` introduces a second `m` module inside this
    // body.
    //
    // Expansion-time resolution finalizes resolutions by re-
    // resolving all imports and macro invocations, sees the
    // introduced ambiguity and reports it as an error.
    m::f!(); // ERROR: `m` is ambiguous.
};

导入

所有 use 声明在此解析阶段完全解析。类型相关路径在此阶段无法解析,将产生错误。

#![allow(unused)]
fn main() {
mod m {
    pub const C: () = ();
    pub enum E { V }
    pub type A = E;
    impl E {
        pub const C: () = ();
    }
}

// Valid imports resolved at expansion-time:
use m::C; // OK.
use m::E; // OK.
use m::A; // OK.
use m::E::V; // OK.

// Valid expressions resolved during type-relative resolution:
let _ = m::A::V; // OK.
let _ = m::E::C; // OK.
}
#![allow(unused)]
fn main() {
mod m {
    pub const C: () = ();
    pub enum E { V }
    pub type A = E;
    impl E {
        pub const C: () = ();
    }
}
// Invalid type-relative imports that can't resolve at expansion-time:
use m::A::V; // ERROR: Unresolved import `m::A::V`.
use m::E::C; // ERROR: Unresolved import `m::E::C`.
}

外部作用域中通过 use 声明引入的名称会被来自内部作用域的同名同命名空间候选遮蔽,除非受到名称解析歧义的限制。

#![allow(unused)]
fn main() {
pub mod m1 {
    pub mod ambig {
        pub const C: u8 = 1;
    }
}

pub mod m2 {
    pub mod ambig {
        pub const C: u8 = 2;
    }
}

// This introduces the name `ambig` in the outer scope.
use m1::ambig;
const _: () = {
    // This shadows `ambig` in the inner scope.
    use m2::ambig;
    // The inner candidate is selected here
    // as the resolution of `ambig`.
    use ambig::C;
    assert!(C == 2);
};
}

在以下情况下,允许在单个作用域内遮蔽通过 use 声明引入的名称:

歧义

在展开时解析期间,存在某些情况,其中存在多个宏定义、use 声明或模块,导入或宏调用的名称可能引用它们,但编译器无法一致地确定哪个候选应该遮蔽另一个。在这些情况下不允许遮蔽,编译器会发出歧义错误。

名称不能通过有歧义的 glob 导入解析。只要名称未被使用,glob 导入允许在同一命名空间中导入冲突的名称。来自有歧义 glob 导入的具有冲突候选的名称仍然可以被非 glob 导入遮蔽,并且可以在不产生错误的情况下使用。错误在使用时发生,而不是在导入时。

#![allow(unused)]
fn main() {
mod m1 {
    pub struct Ambig;
}

mod m2 {
    pub struct Ambig;
}

// OK: This brings conficting names in the same namespace into scope
// but they have not been used yet.
use m1::*;
use m2::*;

const _: () = {
    // The error happens when the name with the conflicting candidates
    // is used.
    let x = Ambig; // ERROR: `Ambig` is ambiguous.
};
}
#![allow(unused)]
fn main() {
mod m1 {
    pub struct Ambig;
}

mod m2 {
    pub struct Ambig;
}

use m1::*;
use m2::*; // OK: No name conflict.
const _: () = {
    // This is permitted, since resolution is not through the
    // ambiguous globs.
    struct Ambig;
    let x = Ambig; // OK.
};
}

多个 glob 导入允许导入相同的名称,并且如果导入的是同一个项(遵循重导出),则允许使用该名称。名称的可见性是导入的最大可见性。

mod m1 {
    pub struct Ambig;
}

mod m2 {
    // This reexports the same `Ambig` item from a second module.
    pub use super::m1::Ambig;
}

mod m3 {
    // These both import the same `Ambig`.
    //
    // The visibility of `Ambig` is `pub` because that is the
    // maximum visibility between these two `use` declarations.
    pub use super::m1::*;
    use super::m2::*;
}

mod m4 {
    // `Ambig` can be used through the `m3` globs and still has
    // `pub` visibility.
    pub use crate::m3::Ambig;
}

const _: () = {
    // Therefore, we can use it here.
    let _ = m4::Ambig; // OK.
};
fn main() {}

外部作用域中有另一个候选可用时,导入和宏调用中的名称不能通过 glob 导入解析。

Note

core::panic!std::panic! 之一由于标准库 prelude被带入作用域,并且用户编写的glob 导入将另一个带入作用域时,rustc 目前允许使用 panic!,即使它是有歧义的。用户编写的 glob 导入优先以解决此歧义。

在 Rust 2021 及更高版本中,core::panic!std::panic! 操作相同。但在早期版本中,它们不同;只有 std::panic! 接受 String 作为格式参数。

例如,这是错误:

extern crate core;
use ::core::prelude::v1::*;
fn main() {
    panic!(std::string::String::new()); // ERROR.
}

这是被接受的:

#![no_std]
extern crate std;
use ::std::prelude::v1::*;
fn main() {
    panic!(std::string::String::new()); // OK.
}

不要依赖此行为;计划是移除它。

有关详细信息,请参阅 Rust issue #147319

#![allow(unused)]
fn main() {
mod glob {
    pub mod ambig {
        pub struct Name;
    }
}

// Outer `ambig` candidate.
pub mod ambig {
    pub struct Name;
}

const _: () = {
    // Cannot resolve `ambig` through this glob
    // because of the outer `ambig` candidate above.
    use glob::*;
    use ambig::Name; // ERROR: `ambig` is ambiguous.
};
}
#![allow(unused)]
fn main() {
// As above, but with macros.
pub mod m {
    macro_rules! f {
        () => {};
    }
    pub(crate) use f;
}
pub mod glob {
    macro_rules! f {
        () => {};
    }
    pub(crate) use f as ambig;
}

use m::f as ambig;

const _: () = {
    use glob::*;
    ambig!(); // ERROR: `ambig` is ambiguous.
};
}

Note

这些歧义错误特定于展开时解析。在解析的后期阶段,给定名称有多个候选不被视为错误。只要导入本身没有歧义,总会有一个单一的无歧义最近解析。

#![allow(unused)]
fn main() {
mod glob {
    pub const AMBIG: u8 = 1;
}

mod outer {
    pub const AMBIG: u8 = 2;
}

use outer::AMBIG;

const C: () = {
    use glob::*;
    assert!(AMBIG == 1);
    //      ^---- This `AMBIG` is resolved during primary resolution.
};
}

名称不能通过有歧义的宏重导出解析。当宏重导出会遮蔽外部作用域中同名的文本宏候选时,宏重导出是有歧义的。

#![allow(unused)]
fn main() {
// Textual macro candidate.
macro_rules! ambig {
    () => {}
}

// Path-based macro candidate.
macro_rules! path_based {
    () => {}
}

pub fn f() {
    // This reexport of the `path_based` macro definition
    // as `ambig` may not shadow the `ambig` macro definition
    // which is resolved via textual macro scope.
    use path_based as ambig;
    ambig!(); // ERROR: `ambig` is ambiguous.
}
}

Note

由于编译器中的实现细节,特别是当前的作用域访问逻辑和支持此行为的复杂性,需要此限制。此歧义错误可能在将来被移除。

宏通过遍历可用作用域以找到可用候选来解析。宏分为两个子命名空间,一个用于函数式宏,另一个用于属性和派生。来自错误子命名空间的解析候选被忽略。

可用的作用域类型按以下顺序访问。每种作用域类型代表一个或多个作用域。

Note

编译器将尝试解析在其关联宏将其引入作用域之前使用的派生辅助。此作用域在正确在作用域内的解析派生辅助候选的作用域之后访问。此行为计划被移除。

有关更多信息,请参阅派生辅助作用域

Note

此访问顺序可能在将来更改,例如根据词法作用域交错访问文本和基于路径的作用域候选。

2018 Edition differences

从 2018 版本开始,当存在 #[no_implicit_prelude] 时,不访问 #[macro_use] prelude。

名称 cfgcfg_attr 在宏属性子命名空间中是保留的。

歧义

名称不能通过宏展开内的有歧义候选解析。当宏展开内的候选会遮蔽来自第一个候选的宏展开外部的同名候选,并且被解析的名称的调用也来自第一个候选的宏展开外部时,宏展开内的候选是有歧义的。

#![allow(unused)]
fn main() {
macro_rules! define_ambig {
    () => {
        macro_rules! ambig {
            () => {}
        }
    }
}

// Introduce outer candidate definition for `ambig` macro invocation.
macro_rules! ambig {
    () => {}
}

// Introduce a second candidate definition for `ambig` inside of a
// macro expansion.
define_ambig!();

// The definition of `ambig` from the second invocation
// of `define_ambig` is the innermost canadidate.
//
// The definition of `ambig` from the first invocation of
// `define_ambig` is the second candidate.
//
// The compiler checks that the first candidate is inside of a macro
// expansion, that the second candidate is not from within the same
// macro expansion, and that the name being resolved is not from
// within the same macro expansion.
ambig!(); // ERROR: `ambig` is ambiguous.
}

反向不被视为有歧义。

#![allow(unused)]
fn main() {
macro_rules! define_ambig {
    () => {
        macro_rules! ambig {
            () => {}
        }
    }
}
// Swap order of definitions.
define_ambig!();
macro_rules! ambig {
    () => {}
}
// The innermost candidate is now less expanded so it may shadow more
// the macro expanded definition above it.
ambig!();
}

如果被解析的调用在最内层候选的展开内,也不被视为有歧义。

#![allow(unused)]
fn main() {
macro_rules! ambig {
    () => {}
}

macro_rules! define_and_invoke_ambig {
    () => {
        // Define innermost candidate.
        macro_rules! ambig {
            () => {}
        }

        // Invocation of `ambig` is in the same expansion as the
        // innermost candidate.
        ambig!(); // OK
    }
}

define_and_invoke_ambig!();
}

即使两个定义来自同一个宏的调用,最外层的候选仍然被认为是“较少展开的“,因为它不在包含最内层候选定义的展开内。

#![allow(unused)]
fn main() {
macro_rules! define_ambig {
    () => {
        macro_rules! ambig {
            () => {}
        }
    }
}
define_ambig!();
define_ambig!();
ambig!(); // ERROR: `ambig` is ambiguous.
}

这也适用于导入,只要名称的最内层候选来自宏展开内。

#![allow(unused)]
fn main() {
macro_rules! define_ambig {
    () => {
        mod ambig {
            pub struct Name;
        }
    }
}

mod ambig {
    pub struct Name;
}

const _: () = {
    // Introduce innermost candidate for
    // `ambig` mod in this macro expansion.
    define_ambig!();
    use ambig::Name; // ERROR: `ambig` is ambiguous.
};
}

用户定义的属性或派生宏不能遮蔽内置的非宏属性(例如 inline)。

// with-helper/src/lib.rs
use proc_macro::TokenStream;
#[proc_macro_derive(WithHelperAttr, attributes(non_exhaustive))]
//                                             ^^^^^^^^^^^^^^
//                                   User-defined attribute candidate.
// ...
pub fn derive_with_helper_attr(_item: TokenStream) -> TokenStream {
    TokenStream::new()
}
// src/lib.rs
#[derive(with_helper::WithHelperAttr)]
#[non_exhaustive] // ERROR: `non_exhaustive` is ambiguous.
struct S;

Note

无论内置属性是什么名称的候选,这都适用:

// with-helper/src/lib.rs
use proc_macro::TokenStream;

#[proc_macro_derive(WithHelperAttr, attributes(helper))]
//                                             ^^^^^^
//                                 User-defined attribute candidate.
// ...
pub fn derive_with_helper_attr(_item: TokenStream) -> TokenStream {
    TokenStream::new()
}
// src/lib.rs
use inline as helper;
//            ^----- Built-in attribute candidate via reexport.

#[derive(with_helper::WithHelperAttr)]
#[helper] // ERROR: `helper` is ambiguous.
struct S;

主要名称解析

Note

这是关于主要名称解析的未来扩展的占位符。

类型相关解析

Note

这是关于类型相关解析的未来扩展的占位符。