Пути
Путь — это последовательность из одного или более сегментов пути, разделённых токенами ::.
Пути используются для ссылки на элементы, значения, типы, макросы и атрибуты.
Два примера простых путей, состоящих только из идентификаторных сегментов:
x;
x::y::z;
Типы путей
Простые пути
Syntax
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() {} } }
Пути в выражениях
Syntax
PathInExpression →
::? PathExprSegment ( :: PathExprSegment )*
PathExprSegment →
PathIdentSegment ( :: GenericArgs )?
PathIdentSegment →
IDENTIFIER | super | self | Self | crate | $crate
GenericArgs →
< >
| < ( GenericArg , )* GenericArg ,? >
GenericArg →
Lifetime | Type | GenericArgsConst | GenericArgsBinding | GenericArgsBounds
GenericArgsConst →
BlockExpression
| LiteralExpression
| - LiteralExpression
| SimplePathSegment
GenericArgsBinding →
IDENTIFIER GenericArgs? = Type
GenericArgsBounds →
IDENTIFIER GenericArgs? : TypeParamBounds
Пути в выражениях позволяют указывать пути с обобщёнными аргументами. Они используются в различных местах в выражениях и образцах.
Токен :: требуется перед открывающим < для обобщённых аргументов, чтобы избежать
неоднозначности с оператором “меньше”. Это в просторечии известно как синтаксис “турбо-рыбы”.
#![allow(unused)] fn main() { (0..10).collect::<Vec<_>>(); Vec::<u8>::with_capacity(1024); }
Порядок обобщённых аргументов ограничен: сначала аргументы времени жизни, затем аргументы типов, затем аргументы констант, затем ограничения равенства.
Аргументы констант должны быть окружены фигурными скобками, если они не являются литералом, выводимой константой или путём из одного сегмента. Выводимая константа не может быть окружена фигурными скобками.
#![allow(unused)] fn main() { mod m { pub const C: usize = 1; } const C: usize = m::C; fn f<const N: usize>() -> [u8; N] { [0; N] } let _ = f::<1>(); // Литерал. let _: [_; 1] = f::<_>(); // Выводимая константа. let _: [_; 1] = f::<(((_)))>(); // Выводимая константа. let _ = f::<C>(); // Путь из одного сегмента. let _ = f::<{ m::C }>(); // Путь из нескольких сегментов должен быть в скобках. }
#![allow(unused)] fn main() { fn f<const N: usize>() -> [u8; N] { [0; _] } let _: [_; 1] = f::<{ _ }>(); // ^ ОШИБКА: `_` не разрешён здесь }
Note
В списке обобщённых аргументов выводимая константа разбирается как выводимый тип, но затем семантически обрабатывается как отдельный вид аргумента обобщённой константы.
Синтетические параметры типа, соответствующие типам impl Trait, являются неявными,
и они не могут быть указаны явно.
Квалифицированные пути
Syntax
QualifiedPathInExpression → QualifiedPathType ( :: PathExprSegment )+
QualifiedPathType → < Type ( as TypePath )? >
QualifiedPathInType → QualifiedPathType ( :: TypePathSegment )+
Полностью квалифицированные пути позволяют устранять неоднозначность пути для реализаций трейтов и для указания канонических путей. При использовании в спецификации типа они поддерживают использование синтаксиса типа, указанного ниже.
#![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. <S as T2>::f(); // Вызывает функцию трейта T2. }
Пути в типах
Syntax
TypePath → ::? TypePathSegment ( :: TypePathSegment )*
TypePathSegment → PathIdentSegment ( ::? ( GenericArgs | TypePathFn ) )?
TypePathFn → ( TypePathFnInputs? ) ( -> TypeNoBounds )?
TypePathFnInputs → Type ( , Type )* ,?
Пути типов используются в определениях типов, ограничениях трейтов, ограничениях параметров типа и квалифицированных путях.
Хотя токен :: разрешён перед обобщёнными аргументами, он не требуется,
поскольку нет неоднозначности, как в 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>; }
Квалификаторы путей
Пути могут обозначаться различными ведущими квалификаторами для изменения смысла того, как они разрешаются.
::
Пути, начинающиеся с ::, считаются глобальными путями, где сегменты пути
начинают разрешаться из места, которое различается в зависимости от редакции. Каждый идентификатор в
пути должен разрешаться в элемент.
2018 Edition differences
В редакции 2015 идентификаторы разрешаются из “корня крейта” (
crate::в редакции 2018), который содержит различные элементы, включая внешние крейты, крейты по умолчанию, такие какstdилиcore, и элементы на верхнем уровне крейта (включая импортыuse).Начиная с редакции 2018, пути, начинающиеся с
::, разрешаются из крейтов во внешней прелюдии. То есть за ними должно следовать имя крейта.
#![allow(unused)] fn main() { pub fn foo() { // В редакции 2018 это обращается к `std` через внешнюю прелюдию. // В редакции 2015 это обращается к `std` через корень крейта. let now = ::std::time::Instant::now(); println!("{:?}", now); } }
// Редакция 2015 mod a { pub fn foo() {} } mod b { pub fn foo() { ::a::foo(); // вызывает функцию foo из `a` // В Rust 2018 `::a` интерпретировался бы как крейт `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 с заглавной “S” используется для ссылки на текущий тип, который реализуется или определяется. Он может использоваться в следующих ситуациях:
- В определении трейта он ссылается на тип, реализующий трейт.
- В реализации он ссылается на тип, который реализуется. При реализации структуры кортежа или единичной структуры он также ссылается на конструктор в пространстве имён значений.
- В определении структуры, перечисления или объединения он ссылается на определяемый тип. Определение не может быть бесконечно рекурсивным (должна быть косвенность).
Область видимости Self ведёт себя аналогично обобщённому параметру; см. раздел область видимости Self для подробностей.
Self может использоваться только как первый сегмент, без предшествующего ::.
Путь Self не может включать обобщённые аргументы (как в Self::<i32>).
#![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`. } } // `Self` находится в области видимости внутри обобщённых параметров определения трейта, // для ссылки на определяемый тип. trait Add<Rhs = Self> { type Output; // `Self` также может ссылаться на ассоциированные элементы // реализуемого типа. fn add(self, rhs: Rhs) -> Self::Output; } struct NonEmptyList<T> { head: T, // Структура может ссылаться на себя (пока это не // бесконечная рекурсия). tail: Option<Box<Self>>, } }
super
super в пути разрешается в родительский модуль.
Он может использоваться только в ведущих сегментах пути, возможно после начального сегмента self.
mod a { pub fn foo() {} } mod b { pub fn foo() { super::a::foo(); // вызывает функцию foo из a } } fn main() {}
super может повторяться несколько раз после первого super или self для ссылки на
модули-предки.
mod a { fn foo() {} mod b { mod c { fn foo() { super::super::foo(); // вызывает функцию foo из a self::super::super::foo(); // вызывает функцию foo из a } } } } fn main() {}
crate
crate разрешает путь относительно текущего крейта.
crate может использоваться только как первый сегмент, без предшествующего ::.
fn foo() {} mod a { fn bar() { crate::foo(); } } fn main() {}
$crate
$crate используется только внутри транскриберов макросов и может использоваться только как первый
сегмент, без предшествующего ::.
$crate развернётся в путь для доступа к элементам из
верхнего уровня крейта, где определён макрос, независимо от того, в каком крейте макрос
вызывается.
pub fn increment(x: u32) -> u32 { x + 1 } #[macro_export] macro_rules! inc { ($x:expr) => ( $crate::increment($x) ) } fn main() { }
Канонические пути
Элементы, определённые в модуле или реализации, имеют канонический путь, который соответствует месту внутри его крейта, где он определён.
Все другие пути к этим элементам являются псевдонимами.
Канонический путь определяется как префикс пути, к которому добавлен сегмент пути, который определяет сам элемент.
Реализации и use-объявления не имеют канонических путей, хотя элементы, которые определяют реализации, имеют их. Элементы, определённые в блочных выражениях, не имеют канонических путей. Элементы, определённые в модуле, который не имеет канонического пути, не имеют канонического пути. Ассоциированные элементы, определённые в реализации, которая ссылается на элемент без канонического пути, например, как на реализуемый тип, реализуемый трейт, параметр типа или ограничение на параметр типа, не имеют канонических путей.
Префикс пути для модулей — это канонический путь к этому модулю.
Для простых реализаций это канонический путь элемента, который реализуется,
окружённый угловыми (<>) скобками.
Для реализаций трейтов это канонический путь элемента, который реализуется,
за которым следует as, за которым следует канонический путь к трейту, всё окружённое
угловыми (<>) скобками.
Канонический путь имеет смысл только в пределах данного крейта. Нет глобального пространства имён между крейтами; канонический путь элемента лишь идентифицирует его в пределах крейта.
// Комментарии показывают канонический путь элемента. 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() {}