Paths
路径
paths.md
commit: 6c77f499eaf64bd89c29a8932e63a9343ee73663
本章译文最后维护日期:2024-04-06
路径是一个或多个由命名空间限定符(::
)逻辑分隔的路径段(path segments)组成的序列(译者注:如果只有一个段的话,::
不是必须的)。如果路径仅由一个路径段组成,则它引用局部控制域(control scope)内的程序项或变量。如果路径包含多个路径段,则总是引用程序项。
仅由标识符段组成的简单路径(simple paths)的两个示例:
x;
x::y::z;net
Types of paths
路径分类
Simple Paths
简单路径
句法
SimplePath :
::
? SimplePathSegment (::
SimplePathSegment)*SimplePathSegment :
IDENTIFIER |super
|self
|crate
|$crate
简单路径可用于可见性标记、属性、宏和 use
程序项中。示例:
#![allow(unused)] fn main() { use std::io::{self, Write}; mod m { #[clippy::cyclomatic_complexity = "0"] pub (in super) fn f1() {} } }
Paths in expressions
表达式中的路径
句法
PathInExpression :\net::
? PathExprSegment (::
PathExprSegment)*PathExprSegment :
PathIdentSegment (::
GenericArgs)?PathIdentSegment :
IDENTIFIER |super
|self
|Self
|crate
|$crate
GenericArgs :
<
>
|<
( GenericArg,
)* GenericArg,
?>
GenericArg :\net Lifetime | Type | GenericArgsConst | GenericArgsBinding | GenericArgsBounds
GenericArgsConst :
BlockExpression
| LiteralExpression
|-
LiteralExpression
| SimplePathSegmentGenericArgsBinding :
IDENTIFIER GenericArgs?=
TypeGenericArgsBounds :
IDENTIFIER GenericArgs?:
TypeParamBounds
表达式中的路径允许指定带有泛型参数的路径。它们在各种表达式和模式中都有使用。
token ::
必须在泛型参数的左尖括号(<
)的前面,以避免和小于号操作符产生混淆。这就是俗称的“涡轮鱼(turbofish)”句法。
#![allow(unused)] fn main() { (0..10).collect::<Vec<_>>();net Vec::<u8>::with_capacity(1024); }
泛型参数的顺序被限制为生存期参数,然后是类型参数,然后是常量参数,再后是相应的约束。
常量实参必须用花括号括起来,除非它们是字面量或单段路径。
impl Trait
这类的合成类型参数是隐式的,不能显式指定。
Qualified paths
限定性路径
句法
QualifiedPathInExpression :
QualifiedPathType (::
PathExprSegment)+QualifiedPathType :
<
Type (as
TypePath)?>
QualifiedPathInType :
QualifiedPathType (::
TypePathSegment)+
完全限定性路径可以用来为 trait实现(trait implementations)消除路径歧义,也可以用来指定规范路径。用在指定具体类型时,它支持使用如下所示的类型句法:
#![allow(unused)] fn main() { struct S; impl S { fn f() { println!("S"); } } trait T1 { fn f() { println!("T1 f"); } } impl T1 for S {} trait T2 { fn f() { println!("T2 f"); } } impl T2 for S {} S::f(); // 调用固有实现 <S as T1>::f(); // 调用T1的 trait函数 <S as T2>::f(); // 调用T2的 trait函数 }
Paths in types
类型中的路径
句法
TypePath :
::
? TypePathSegment (::
TypePathSegment)*TypePathSegment :
PathIdentSegment (::
? (GenericArgs | TypePathFn))?TypePathFn :
(
TypePathFnInputs?)
(->
TypeNoBounds)?
类型路径用于类型定义、trait约束(trait bound)、类型参数约束,以及限定性路径。
尽管在泛型参数之前允许使用 token ::
,但它不是必需的,因为在类型路径中不存在像 PathInExpression 句法中那样的歧义。
#![allow(unused)] fn main() { mod ops { pub struct Range<T> {f1: T} pub trait Index<T> {} pub struct Example<'a> {f1: &'a i32} } struct S; impl ops::Index<ops::Range<usize>> for S { /*...*/ } fn i<'a>() -> impl Iterator<Item = ops::Example<'a>> { // ... const EXAMPLE: Vec<ops::Example<'static>> = Vec::new(); EXAMPLE.into_iter() } type G = std::boxed::Box<dyn std::ops::FnOnce(isize) -> isize>; }
Path qualifiers
路径限定符
路径可以被各种能改变其解析方式的前导限定符限定。
::
以 ::
开头的路径被认为是全局路径,其中的路径首段在不同的版次中的解析方式有所不同。但路径中的每个标识符都必须解析为一个程序项。
版次差异: 在2015版中,标识符解析从“create 根模块(crate root)”(2018版中表示为
crate::
)开始,“create 根模块(crate root)”中包含了一系列不同的程序项,包括外部crate、默认create(如std
或core
),以及 crate下的各种顶层程序项(包括use
导入)。从 2018 版开始,以
::
开头的路径被解析为外部预导入包中的一个 crate。也就是说,其后必须跟一个 crate的名称。
#![allow(unused)] fn main() { pub fn foo() { // 在 2018版中,这种访问 `std` 的方法是通过外部预导入包的形式达到的 // 在 2015版中, 这种访问 `std` 的方法是通过crate根的形式达到的 let now = ::std::time::Instant::now(); println!("{:?}", now); } }
// 2015 Edition mod a { pub fn foo() {} } mod b { pub fn foo() { crate::a::foo(); // 调用 `a`'的 foo 函数 // 在 Rust 2018 中, `::a` 会被解析为 crate `a`. } } fn main() {}
self
self
表示路径是相对于当前模块的路径。self
仅可以用作路径的首段,不能有前置 ::
。
在方法体中,由 self
段组成的路径被解析为此方法的 self参数。
fn foo() {} fn bar() { self::foo(); } struct S(bool); impl S { fn baz(self) { self.0; } } fn main() {}
Self
Self
(首字母大写)用于指代 trait 和实现中的类型。
Self
仅可以用作路径的首段,不能有前置 ::
。
#![allow(unused)] fn main() { trait T { type Item; const C: i32; // `Self` 将是实现 `T` 的任何类型。 fn new() -> Self; // `Self::Item` 将是实现中指定的类型的别名。 fn f(&self) -> Self::Item; } struct S; impl T for S { type Item = i32; const C: i32 = 9; fn new() -> Self { // `Self` 是类型 `S`. S } fn f(&self) -> Self::Item { // `Self::Item` 是类型 `i32`. Self::C // `Self::C` 是常量值 `9`. } } }
super
super
在路径中被解析为父模块。它只能用于路径的前导段,可以置于 self
路径段之后。
mod a { pub fn foo() {} } mod b { pub fn foo() { super::a::foo(); // 调用 a'的 foo 函数 } } fn main() {}
super
可以在第一个 super
或 self
之后重复多次,以引用祖先模块。
mod a { fn foo() {} mod b { mod c { fn foo() { super::super::foo(); // 调用 a'的 foo 函数 self::super::super::foo(); // 调用 a'的 foo 函数 } } } } fn main() {}
crate
crate
解析相对于当前 crate 的路径。crate 仅能用作路径的首段,不能有前置 ::
。
fn foo() {} mod a { fn bar() { crate::foo(); } } fn main() {}
$crate
$crate 仅用在宏转码器(macro transcriber)中,且仅能用作路径首段,不能有前置 ::
。$crate
将被扩展为从定义宏的 crate 的顶层访问该 crate 中的各程序项的路径,而不用去考虑宏调用发生时所在的现场 crate。
pub fn increment(x: u32) -> u32 { x + 1 } #[macro_export] macro_rules! inc { ($x:expr) => ( $crate::increment($x) ) } fn main() { }
Canonical paths
规范路径
定义在模块或者实现中的程序项都有一个规范路径,该路径对应于其在其 crate 中定义的位置。在该路径之外,所有指向这些程序项的路径都是别名(路径)。规范路径被定义为一个路径前缀后跟一个代表程序项本身的那段路径段。
尽管实现所定义/实现的程序项有规范路径,但实现作为一个整体,自身没有规范路径。use声明也没有规范路径。块表达式中定义的程序项也没有规范路径。在没有规范路径的模块中定义的程序项也没有规范路径。在实现中定义的关联项(associated items)如果它指向没有规范路径的程序项——例如,实现类型(implementing type)、被实现的 trait、类型参数或类型参数上的约束——那它也没有规范路径。
模块的路径前缀就是该模块的规范路径。对于裸实现(bare implementations)来说,它里面的程序项的路径前缀是:它当前实现的程序项的规范路径,再用尖括号(<>
)括把此路径括起来。对于 trait实现来说,它内部的程序项的路径前缀是:当前实现的程序项的规范路径后跟一个 as
,再后跟 trait 本身的规范路径,然后整个使用尖括号(<>
)括起来。
规范路径只在给定的 crate 中有意义。在 crate 之间没有全局命名空间,所以程序项的规范路径只在其 crate 中可标识。
// 注释解释了程序项的规范路径 mod a { // crate::a pub struct Struct; // crate::a::Struct pub trait Trait { // crate::a::Trait fn f(&self); // crate::a::Trait::f } impl Trait for Struct { fn f(&self) {} // <crate::a::Struct as crate::a::Trait>::f } impl Struct { // 译者注:这是一个裸实现 fn g(&self) {} // <crate::a::Struct>::g } } mod without { // crate::without fn canonicals() { // crate::without::canonicals struct OtherStruct; // None trait OtherTrait { // None fn g(&self); // None } impl OtherTrait for OtherStruct { fn g(&self) {} // None } impl OtherTrait for crate::a::Struct { fn g(&self) {} // None } impl crate::a::Trait for OtherStruct { fn f(&self) {} // None } } } fn main() {}