경로

경로 (Paths)

함수나 타입, 변수의 이름을 쓸 때 우리는 항상 경로(path)를 써요. 경로는 :: 토큰으로 구분된 **하나 이상의 경로 세그먼트(segment)**의 나열인데, 아이템, 값, 타입, 매크로, 속성(attribute)을 가리키는 데 쓰입니다.

식별자 세그먼트만으로 이루어진 간단한 경로 두 개를 보면 이렇게 생겼어요.

x;
x::y::z;

출처: Rust Reference

본문

경로의 종류

단순 경로 (Simple paths)

문법

SimplePath →
    ::? SimplePathSegment ( :: SimplePathSegment )*

SimplePathSegment →
    IDENTIFIER | super | self | crate | $crate

단순 경로는 가시성(visibility) 마커, 속성, 매크로, 그리고 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 →
    ::? PathExprSegment ( :: PathExprSegment )*

PathExprSegment →
    PathIdentSegment ( :: GenericArgs )?

PathIdentSegment →
    IDENTIFIER | super | self | Self | crate | $crate

GenericArgs →
      < GenericArgList? >
    | ( TypeList? ) ( -> TypeNoBounds )?

GenericArgList →
    ( GenericArg , )* GenericArg ,?

TypeList →
    ( Type , )* Type ,?

GenericArg →
    Lifetime | Type | GenericArgsConst | GenericArgsBinding | GenericArgsBounds

GenericArgsConst →
      BlockExpression
    | LiteralExpression
    | - LiteralExpression
    | SimplePathSegment

GenericArgsBinding →
    TypePathSegment = Type

GenericArgsBounds →
    TypePathSegment : Bounds?

표현식에서의 경로는 제네릭 인자를 지정할 수 있는 경로예요. 표현식패턴의 여러 곳에서 사용됩니다.

터보피시(turbofish) 문법

제네릭 인자의 < 앞에는 :: 토큰이 필요해요. 그래야 보다 작음(<) 연산자와의 모호함을 피할 수 있거든요. 이걸 흔히 '터보피시(turbofish)' 문법이라고 불러요.

#![allow(unused)]
fn main() {
(0..10).collect::<Vec<_>>();
Vec::<u8>::with_capacity(1024);
}

제네릭 인자의 순서

제네릭 인자의 순서는 제한되어 있어요. 평생(lifetime) 인자 → 타입 인자 → 상수(const) 인자 → 동등 제약(equality constraint) 순서로 옵니다.

상수 인자 규칙

상수 인자는 리터럴, 추론된 상수(inferred const), 또는 단일 세그먼트 경로가 아니라면 중괄호로 둘러싸야 해요. 반대로 추론된 상수는 중괄호로 둘러쌀 수 없습니다.

#![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::<{ _ }>();
//                    ^ ERROR `_` not allowed here
}

참고: 제네릭 인자 목록에서 추론된 상수추론된 타입으로 파싱되지만, 의미상으로는 별도의 상수 제네릭 인자 종류로 취급돼요.

impl Trait 매개변수

impl Trait 타입에 대응하는 합성 타입 매개변수는 암시적이라서, 이를 명시적으로 지정할 수는 없어요.

정규화된 경로 (Qualified paths)

문법

QualifiedPathInExpression → QualifiedPathType ( :: PathExprSegment )+

QualifiedPathType → < Type ( as TypePath )? >

QualifiedPathInType → QualifiedPathType ( :: TypePathSegment )+

정규화된(fully qualified) 경로는 트레이트 구현(trait implementation)의 경로를 모호함 없이 구분하고, 표준 경로(canonical path)를 지정하는 데 사용해요. 타입 지정에 쓰일 때는 아래의 타입 문법을 지원합니다.

#![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();  // 고유(inherent) 구현을 호출
<S as T1>::f();  // T1 트레이트 함수를 호출
<S as T2>::f();  // T2 트레이트 함수를 호출
}

타입에서의 경로 (Paths in types)

문법

TypePath → ::? TypePathSegment ( :: TypePathSegment )*

TypePathSegment → PathIdentSegment ( ::? GenericArgs )?

타입 경로는 타입 정의, 트레이트 경계(trait bound), 정규화된 경로 안에서 사용돼요.

터보피시 필요 없음

경로 표현식과는 달리 여기서는 < 앞에 ::를 써도 되지만 필수는 아니에요. 모호함이 없기 때문입니다.

#![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)

경로에는 다양한 **선행 수식어(qualifier)**를 붙여서 해석 방식을 바꿀 수 있어요.

참고: use 선언에서는 self, super, crate, $crate에 대해 추가적인 동작과 제한이 있어요.

::

문법
[::] → ::

::로 시작하는 경로는 **전역 경로(global path)**로 간주됩니다. 세그먼트가 해석되기 시작하는 위치는 에디션에 따라 달라져요. 경로의 각 식별자는 아이템으로 해석되어야 합니다.

2018 에디션 차이

2015 에디션에서는 식별자가 크레이트 루트(crate root)(2018 에디션의 crate::)부터 해석됩니다. 여기에는 외부 크레이트, stdcore 같은 기본 크레이트, 크레이트 최상위 아이템(use 임포트 포함)이 들어 있어요.

2018 에디션부터 ::로 시작하는 경로는 extern prelude의 크레이트에서 해석됩니다. 즉, 그 뒤에 크레이트 이름이 와야 해요.

#![allow(unused)]
fn main() {
pub fn foo() {
    // 2018 에디션에서 이건 extern prelude를 통해 `std`에 접근
    // 2015 에디션에서 이건 크레이트 루트를 통해 `std`에 접근
    let now = ::std::time::Instant::now();
    println!("{:?}", now);
}
}
// 2015 에디션
mod a {
    pub fn foo() {}
}
mod b {
    pub fn foo() {
        ::a::foo(); // `a`의 foo 함수 호출
        // Rust 2018에서는 `::a`를 크레이트 `a`로 해석
    }
}
fn main() {}

self

self는 경로를 현재 모듈 기준으로 해석해요.

self는 경로의 첫 세그먼트(앞에 :: 없이) 또는 마지막 세그먼트(:: 뒤에)로만 사용될 수 있어요.

self가 경로의 마지막 세그먼트로 오면, 앞 세그먼트가 가리키는 엔티티를 참조합니다. 이때 앞 경로는 모듈, 열거형(enumeration), 또는 트레이트로 해석되어야 해요.

mod m {
    pub enum E { V1 }
    pub trait Tr {}
    pub(in crate::m::self) fn g() {} // OK: 모듈은 `self`의 부모가 될 수 있다.
}
type Ty = m::E::self; // OK: 열거형은 `self`의 부모가 될 수 있다.
fn f<T: m::Tr::self>() {} // OK: 트레이트는 `self`의 부모가 될 수 있다.
fn main() { let _: Ty = m::E::V1; }
struct S;
type Ty = S::self; // ERROR: 구조체는 `self`의 부모가 될 수 없다.
fn main() {}

참고: use 선언에서의 self에 대한 추가 규칙은 items.use.self를 참고해요.

메서드 본문에서 단일 self 세그먼트만으로 이루어진 경로는 메서드의 self 매개변수를 가리켜요.

fn foo() {}
fn bar() {
    self::foo();
}
struct S(bool);
impl S {
  fn baz(self) {
        self.0;
    }
}
fn main() {}

Self

대문자 S로 시작하는 Self현재 구현하거나 정의하고 있는 타입을 가리켜요. 다음과 같은 상황에서 사용할 수 있습니다.

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부모 모듈을 가리켜요.

super는 경로의 선행 세그먼트에서만 사용할 수 있어요(경우에 따라 초기 self 세그먼트 다음에 올 수 있어요).

mod a {
    pub fn foo() {}
}
mod b {
    pub fn foo() {
        super::a::foo(); // a의 foo 함수 호출
    }
}
fn main() {}

superself 다음에 super를 여러 번 반복해서 조상 모듈을 가리킬 수도 있어요.

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는 앞에 :: 없이 첫 세그먼트로만 사용할 수 있어요.

fn foo() {}
mod a {
    fn bar() {
        crate::foo();
    }
}
fn main() {}

$crate

$crate매크로 트랜스크라이버(transcriber) 안에서만 사용되며, 앞에 :: 없이 첫 세그먼트로만 쓸 수 있어요.

$crate매크로가 정의된 크레이트의 최상위에서 아이템에 접근하는 경로로 확장됩니다. 매크로가 어느 크레이트에서 호출되든 상관없이요.

pub fn increment(x: u32) -> u32 {
    x + 1
}

#[macro_export]
macro_rules! inc {
    ($x:expr) => ( $crate::increment($x) )
}
fn main() { }

표준 경로 (Canonical paths)

모듈이나 구현에서 정의된 각 아이템은 크레이트 내 어디에 정의되었는지에 대응하는 표준 경로를 가져요.

이 아이템들을 가리키는 **다른 모든 경로는 별칭(alias)**입니다.

표준 경로는 경로 접두어(prefix) 뒤에 아이템이 스스로 정의하는 경로 세그먼트를 붙인 것으로 정의돼요.

구현(implementation)use 선언은 표준 경로가 없습니다. 다만 구현이 정의하는 아이템들은 표준 경로를 가져요. 블록 표현식 안에 정의된 아이템도 표준 경로가 없어요. 표준 경로가 없는 모듈에 정의된 아이템 역시 표준 경로가 없습니다. 그리고 표준 경로가 없는 아이템(예: 구현 대상 타입, 구현 중인 트레이트, 타입 매개변수나 타입 매개변수의 경계)을 참조하는 구현에 정의된 연관 아이템도 표준 경로가 없어요.

  • 모듈의 경로 접두어는 그 모듈의 표준 경로예요.
  • 고유(bare) 구현의 경우, 구현 대상 아이템의 표준 경로를 꺾쇠(<>)로 감싼 것이에요.
  • 트레이트 구현의 경우, 구현 대상 아이템의 표준 경로 + 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; // 없음

        trait OtherTrait { // 없음
            fn g(&self); // 없음
        }

        impl OtherTrait for OtherStruct {
            fn g(&self) {} // 없음
        }

        impl OtherTrait for crate::a::Struct {
            fn g(&self) {} // 없음
        }

        impl crate::a::Trait for OtherStruct {
            fn f(&self) {} // 없음
        }
    }
}

fn main() {}

더 알아보기