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

作用域

作用域是源文本中可以以其名称引用某命名实体的区域。以下各节提供了作用域规则和行为的详细信息,这些取决于实体的类型及其声明位置。名称如何解析为实体的过程在名称解析章节中描述。有关用于运行析构函数的“丢弃作用域“的更多信息,请参见析构函数章节。

项作用域

直接在模块中声明的的名称具有从模块开头延伸到模块末尾的作用域。这些项也是模块的成员,可以通过从其模块开始的路径引用。

作为语句声明的项的名称具有从该语句所在块的开头延伸到块末尾的作用域。

在同一模块或块内引入与同命名空间中另一个项名称重复的项是错误的。星号 glob 导入对处理重复名称和遮蔽有特殊行为,更多细节请参见链接的章节。

模块中的项可以遮蔽预导入中的项。

外部模块的项名称不在嵌套模块的作用域中。可以使用路径引用另一个模块中的项。

关联项作用域

关联项没有作用域,只能通过从它们关联的类型或 trait 开始的路径引用。方法也可以通过调用表达式引用。

类似于模块或块内的项,在 trait 或实现中引入与同一命名空间中 trait 或 impl 中另一个项重复的项是错误的。

模式绑定作用域

局部变量模式绑定的作用域取决于它使用的上下文:

  • let 语句绑定从 let 语句之后开始,直到声明它的块结束。
  • for 绑定在循环体内部。
  • match 守卫 let 绑定在后续守卫条件和匹配分支表达式中有效。

局部变量作用域不扩展到项声明内部。

模式绑定遮蔽

模式绑定允许遮蔽作用域中的任何名称,但以下情况会导致错误:

以下示例说明了局部绑定如何遮蔽项声明:

#![allow(unused)]
fn main() {
fn shadow_example() {
    // 由于作用域中还没有局部变量,这将解析为函数。
    foo(); // 打印 `function`
    let foo = || println!("closure");
    fn foo() { println!("function"); }
    // 这将解析为局部闭包,因为它遮蔽了该项。
    foo(); // 打印 `closure`
}
}

泛型参数作用域

泛型参数在 GenericParams 列表中声明。泛型参数的作用域在其声明的项内部。

所有参数都在泛型参数列表中的作用域内,无论声明顺序如何。以下展示了一些在声明之前引用参数的示例:

#![allow(unused)]
fn main() {
// 'b 约束在其声明之前被引用。
fn params_scope<'a: 'b, 'b>() {}

trait SomeTrait<const Z: usize> {}
// 常量 N 在其声明之前在 trait 约束中被引用。
fn f<T: SomeTrait<N>, const N: usize>() {}
}

泛型参数也在类型约束和 where 子句的作用域中,例如:

#![allow(unused)]
fn main() {
trait SomeTrait<'a, T> {}
// `SomeTrait` 的 `<'a, U>` 引用 `bounds_scope` 的 'a 和 U 参数。
fn bounds_scope<'a, T: SomeTrait<'a, U>, U>() {}

fn where_scope<'a, T, U>()
    where T: SomeTrait<'a, U>
{}
}

在函数内部声明的引用其外部作用域的泛型参数是错误的。

#![allow(unused)]
fn main() {
fn example<T>() {
    fn inner(x: T) {} // 错误:不能使用外部函数的泛型参数
}
}

泛型参数遮蔽

遮蔽泛型参数是错误的,但函数内部声明的项允许遮蔽该函数的泛型参数名称。

#![allow(unused)]
fn main() {
fn example<'a, T, const N: usize>() {
    // 函数内部的项允许遮蔽作用域中的泛型参数。
    fn inner_lifetime<'a>() {} // 正确
    fn inner_type<T>() {} // 正确
    fn inner_const<const N: usize>() {} // 正确
}
}
#![allow(unused)]
fn main() {
trait SomeTrait<'a, T, const N: usize> {
    fn example_lifetime<'a>() {} // 错误:'a 已在使用中
    fn example_type<T>() {} // 错误:T 已在使用中
    fn example_const<const N: usize>() {} // 错误:N 已在使用中
    fn example_mixed<const T: usize>() {} // 错误:T 已在使用中
}
}

生命周期作用域

生命周期参数在 GenericParams 列表和高阶 trait 约束中声明。

'static 生命周期和占位生命周期 '_ 具有特殊含义,不能作为参数声明。

生命周期泛型参数作用域

常量静态项以及 const 上下文 仅允许 'static 生命周期引用,因此没有其他生命周期可以在它们的作用域中。关联常量确实允许引用在其 trait 或实现中声明的生命周期。

高阶 trait 约束作用域

声明为高阶 trait 约束的生命周期参数的作用域取决于其使用场景。

  • 作为 TypeBoundWhereClauseItem,声明的生命周期在类型和类型约束的作用域中。
  • 作为 TraitBound,声明的生命周期在约束类型路径的作用域中。
  • 作为 BareFunctionType,声明的生命周期在函数参数和返回类型的作用域中。
#![allow(unused)]
fn main() {
trait Trait<'a>{}

fn where_clause<T>()
    // 'a 在类型和类型约束中都在作用域中。
    where for <'a> &'a T: Trait<'a>
{}

fn bound<T>()
    // 'a 在约束内部的作用域中。
    where T: for <'a> Trait<'a>
{}

struct Example<'a> {
    field: &'a u32
}

// 'a 在参数和返回类型中都在作用域中。
type FnExample = for<'a> fn(x: Example<'a>) -> Example<'a>;
}

impl trait 限制

Impl trait 类型只能引用在函数或实现上声明的生命周期。

#![allow(unused)]
fn main() {
trait Trait1 {
    type Item;
}
trait Trait2<'a> {}

struct Example;

impl Trait1 for Example {
    type Item = Element;
}

struct Element;
impl<'a> Trait2<'a> for Element {}

// 此处的 `impl Trait2` 不允许引用 'b,但允许引用 'a。
fn foo<'a>() -> impl for<'b> Trait1<Item = impl Trait2<'a> + use<'a>> {
    // ...
   Example
}
}

循环标签作用域

循环标签可以由循环表达式声明。循环标签的作用域从它被声明的点开始直到循环表达式的末尾。作用域不会扩展到闭包async 块常量参数const 上下文以及定义它的 for 循环的迭代器表达式中。

#![allow(unused)]
fn main() {
'a: for n in 0..3 {
    if n % 2 == 0 {
        break 'a;
    }
    fn inner() {
        // 在此处使用 'a 会是错误。
        // break 'a;
    }
}

// 标签在 `while` 循环的表达式中处于作用域中。
'a: while break 'a {}         // 循环不运行。
'a: while let _ = break 'a {} // 循环不运行。

// 标签在定义它的 `for` 循环中不在作用域中:
'a: for outer in 0..5 {
    // 这将中断外部循环,跳过内部循环并停止外部循环。
    'a: for inner in { break 'a; 0..1 } {
        println!("{}", inner); // 这不会运行。
    }
    println!("{}", outer); // 这也不会运行。
}

}

循环标签可以遮蔽外部作用域中相同名称的标签。对标签的引用指向最接近的定义。

#![allow(unused)]
fn main() {
// 循环标签遮蔽示例。
'a: for outer in 0..5 {
    'a: for inner in 0..5 {
        // 这将终止内部循环,但外部循环继续运行。
        break 'a;
    }
}
}

预导入作用域

预导入将实体带入每个模块的作用域。这些实体不是模块的成员,而是在名称解析期间被隐式查询。

预导入名称可以被模块中的声明遮蔽。

预导入是分层的,如果一个预导入包含与另一个预导入同名的实体,则一个可以遮蔽另一个。预导入可能遮蔽其他预导入的顺序如下,其中较前的条目可以遮蔽较后的条目:

  1. 外部 crate 预导入
  2. 工具预导入
  3. macro_use 预导入
  4. 标准库预导入
  5. 语言预导入

macro_rules 作用域

macro_rules 宏的作用域在声明宏章节中描述。行为取决于 macro_usemacro_export 属性的使用。

派生宏辅助属性

派生宏辅助属性在其对应 derive 属性指定的项中处于作用域中。作用域从 derive 属性之后开始直到该项目的末尾。

辅助属性遮蔽作用域中同名的其他属性。

Self 作用域

尽管 Self 是具有特殊含义的关键字,但它与名称解析的交互方式类似于普通名称。

structenumuniontrait实现的定义中,隐式的 Self 类型被视为类似于泛型参数,并以与泛型类型参数相同的方式处于作用域中。

实现的值命名空间中,隐式的 Self 构造函数在实现体(实现的关联项)内部处于作用域中。

#![allow(unused)]
fn main() {
// 结构体定义中的 Self 类型。
struct Recursive {
    f1: Option<Box<Self>>
}

// 泛型参数中的 Self 类型。
struct SelfGeneric<T: Into<Self>>(T);

// 实现中的 Self 值构造函数。
struct ImplExample();
impl ImplExample {
    fn example() -> Self { // Self 类型
        Self() // Self 值构造函数
    }
}
}