네임스페이스

네임스페이스 (Namespaces)

한 프로그램 안에서 같은 이름이 타입이면서 동시에 값일 수는 없을까요? 바로 그게 가능하게 해 주는 게 네임스페이스(namespace) 개념이에요. 이름을 종류별로 분리해 두면, 같은 이름이 다른 네임스페이스에서 충돌하지 않거든요. 이 글에서는 Rust의 네임스페이스가 어떻게 나뉘는지 살펴볼게요.

출처: Rust Reference

본문

네임스페이스는 선언된 이름들의 논리적 묶음(logical grouping) 이야. 이름은 그 이름이 가리키는 엔티티의 종류에 따라 서로 다른 네임스페이스로 분리돼요. 덕분에 한 네임스페이스에서 이름이 나타나는 것이 다른 네임스페이스의 같은 이름과 충돌하지 않아요.

각기 다른 종류의 엔티티를 담는 여러 네임스페이스가 있는데, 이름을 사용할 때는 이름 해석(name resolution) 챕터에서 설명한 것처럼 문맥에 따라 서로 다른 네임스페이스에서 그 이름의 선언을 찾아요.

네임스페이스의 종류 (Kinds)

네임스페이스와 그에 해당하는 엔티티 목록이에요.

타입 네임스페이스 (Type namespace)

  • 모듈 선언
  • 외부 크레이트 선언
  • 외부 크레이트 프렐류드 아이템
  • 구조체, 유니온, 열거형, 열거형 변형 선언
  • 트레이트 아이템 선언
  • 타입 별칭
  • 연관 타입 선언
  • 내장 타입: boolean, numeric, char, str
  • 제네릭 타입 매개변수
  • Self 타입
  • 툴 속성 모듈

값 네임스페이스 (Value namespace)

  • 함수 선언
  • 상수 아이템 선언
  • 정적 아이템 선언
  • 구조체 생성자
  • 열거형 변형 생성자
  • Self 생성자
  • 제네릭 const 매개변수
  • 연관 const 선언
  • 연관 함수 선언
  • 지역 바인딩 — let, if let, while let, for, match arm, 함수 매개변수, 클로저 매개변수
  • 캡처된 클로저 변수

매크로 네임스페이스 (Macro namespace)

  • macro_rules 선언
  • 내장 속성
  • 툴 속성
  • 함수형 절차적 매크로(function-like procedural macros)
  • derive 매크로
  • derive 매크로 헬퍼
  • 속성 매크로

수명 네임스페이스 (Lifetime namespace)

  • 제네릭 수명 매개변수

레이블 네임스페이스 (Label namespace)

  • 루프 레이블
  • 블록 레이블

서로 다른 네임스페이스의 겹치는 이름이 어떻게 모호함 없이 쓰일 수 있는지 예를 보여 드릴게요.

#![allow(unused)]
fn main() {
// Foo introduces a type in the type namespace and a constructor in the value
// namespace.
struct Foo(u32);

// The `Foo` macro is declared in the macro namespace.
macro_rules! Foo {
    () => {};
}

// `Foo` in the `f` parameter type refers to `Foo` in the type namespace.
// `'Foo` introduces a new lifetime in the lifetime namespace.
fn example<'Foo>(f: Foo) {
    // `Foo` refers to the `Foo` constructor in the value namespace.
    let ctor = Foo;
    // `Foo` refers to the `Foo` macro in the macro namespace.
    Foo!{}
    // `'Foo` introduces a label in the label namespace.
    'Foo: loop {
        // `'Foo` refers to the `'Foo` lifetime parameter, and `Foo`
        // refers to the type namespace.
        let x: &'Foo Foo;
        // `'Foo` refers to the label.
        break 'Foo;
    }
}
}

이 예시처럼 Foo 하나가 문맥에 따라 타입·생성자·매크로로, 'Foo가 수명·레이블로 다르게 해석되는 게 각각의 네임스페이스 덕분이에요.

네임스페이스가 없는 이름 (Named entities without a namespace)

필드 (Fields) — 구조체, 열거형, 유니온의 필드는 이름이 있지만 명시적인 네임스페이스에 살지 않아요. 필드 표현식(field expression)으로만 접근할 수 있고, 그 표현식은 접근하는 특정 타입의 필드 이름만 살펴봐요.

사용 선언 (Use declarations)use 선언은 스코프로 가져오는 이름 있는 별칭을 가지지만, use 아이템 자체는 특정 네임스페이스에 속하지 않아요. 대신 가져오는 아이템의 종류에 따라 여러 네임스페이스에 별칭을 도입해요.

하위 네임스페이스 (Sub-namespaces)

매크로 네임스페이스는 뱅 스타일 매크로(bang-style macros) 를 위한 것과 속성(attributes) 을 위한 것, 두 개의 하위 네임스페이스로 나뉘어요. 속성을 해석할 때는 스코프 안의 뱅 스타일 매크로를 무시하고, 반대로 뱅 스타일 매크로를 해석할 때는 스코프 안의 속성 매크로를 무시해요. 이로써 한 쪽 스타일이 다른 쪽을 가리지(shadow) 못 해요.

예를 들어 cfg 속성과 cfg 매크로는 매크로 네임스페이스에서 같은 이름을 가진 서로 다른 두 엔티티인데도, 각자의 문맥에서 사용될 수 있어요.

참고: 하위 네임스페이스가 있더라도, use import는 여전히 모듈이나 블록 안에 같은 이름의 중복 바인딩을 만들 수 없어요.

#[macro_export]
macro_rules! mymac {
    () => {};
}

use myattr::mymac; // error[E0252]: the name `mymac` is defined multiple times.

더 알아보기 (Learn more)