use 선언(Use declarations)

use 선언(Use declarations)

use 선언은 어떤 다른 경로와 같은 뜻이 되는 로컬 이름 바인딩을 하나 이상 만들어요. 보통 모듈 아이템을 참조할 때 필요한 경로를 줄이려고 사용하죠. 이 선언은 모듈과 블록 안에, 대개 맨 위에 나타나요. use 선언은 가져오기(import)라고도 부르고, public이면 재수출(re-export)이라고도 불러요.

문법을 먼저 볼게요.

UseDeclaration → use UseTree ;
UseTree → ( SimplePath? :: )? *
       | ( SimplePath? :: )? { ( UseTree ( , UseTree )* ,? )? }
       | SimplePath ( as ( IDENTIFIER | _ ) )?

출처: Rust Reference

본문

use의 편의 문법들

use 선언은 몇 가지 편리한 단축 문법을 지원해요.

  • 공통 접두사를 가진 경로 목록을 한 번에 바인딩: 중괄호 문법으로 use a::b::{c, d, e::f, g::h::i};
  • 공통 접두사와 그 부모 모듈을 함께 바인딩: self 키워드를 써서 use a::b::{self, c, d::e};
  • 대상 이름을 새 로컬 이름으로 다시 바인딩: use p::q::r as x;. 앞의 두 기능과도 조합할 수 있어요. use a::b::{self as ab, c as abc}
  • 주어진 접두사와 일치하는 모든 경로를 바인딩: 별표 와일드카드 문법 use a::b::*;
  • 앞의 기능들을 여러 번 중첩: use a::b::{self as ab, c, d::{*, e::f}};

예시를 볼게요.

use std::collections::hash_map::{self, HashMap};

fn foo<T>(_: T){}
fn bar(map1: HashMap<String, usize>, map2: hash_map::HashMap<String, usize>){}

fn main() {
    // use 선언은 함수 안에도 있을 수 있어요.
    use std::option::Option::{Some, None};

    // 'foo(vec![std::option::Option::Some(1.0f64),
    // std::option::Option::None]);'와 동등해요.
    foo(vec![Some(1.0f64), None]);

    // `hash_map`과 `HashMap` 둘 다 스코프에 있어요.
    let map1 = HashMap::new();
    let map2 = hash_map::HashMap::new();
    bar(map1, map2);
}

use 가시성

아이템처럼 use 선언도 기본적으로 포함하는 모듈에서 private해요. 아이템과 마찬가지로 pub 키워드를 붙이면 public해지고, 그런 use 선언은 이름을 재수출하는 역할을 해요. 따라서 public use 선언은 어떤 public 이름을 다른 대상 정의로 리다이렉트할 수 있어요. 심지어 다른 모듈 안에 있는, private한 정식 경로를 가진 정의라도요.

이런 리다이렉트의 연속이 순환을 이루거나 모호하게 해석되면 컴파일 오류가 돼요.

재수출 예시를 볼게요.

mod quux {
    pub use self::foo::{bar, baz};
    pub mod foo {
        pub fn bar() {}
        pub fn baz() {}
    }
}

fn main() {
    quux::bar();
    quux::baz();
}

여기서 quux 모듈은 foo에 정의된 두 public 이름을 재수출해요.

use 경로

use 아이템에서 허용되는 경로는 SimplePath 문법을 따르고, 표현식에서 쓰는 경로와 비슷해요. 다음을 위한 바인딩을 만들 수 있어요.

  • 이름 붙일 수 있는 아이템
  • 열거형 변형(enum variant)
  • 내장 타입
  • 속성(attribute)
  • 파생 매크로(derive macro)
  • macro_rules

반면 다음은 가져올 수 없어요. 연관 아이템, 제네릭 매개변수, 로컬 변수, Self가 있는 경로, 도구 속성(tool attribute). 더 많은 제한은 아래에서 볼게요.

use는 가져온 개체에서 모든 네임스페이스에 대한 바인딩을 만들어요. 단, self 가져오기는 타입 네임스페이스에서만 가져와요. 예를 들어 한 이름을 두 네임스페이스에 바인딩하는 걸 볼게요.

#![allow(unused)]
fn main() {
mod stuff {
    pub struct Foo(pub i32);
}

// `Foo` 타입과 `Foo` 생성자를 가져와요.
use stuff::Foo;

fn example() {
    let ctor = Foo; // 값 네임스페이스의 `Foo`를 사용해요.
    let x: Foo = ctor(123); // 타입 네임스페이스의 `Foo`를 사용해요.
}
}

2018 에디션 차이

2015 에디션에서는 use 경로가 크레이트 루트 기준으로 상대적이에요.

mod foo {
    pub mod example { pub mod iter {} }
    pub mod baz { pub fn foobaz() {} }
}
mod bar {
    // 크레이트 루트에서 `foo`를 해석해요.
    use foo::example::iter;
    // `::` 접두사는 크레이트 루트에서 `foo`를
    // 명시적으로 해석해요.
    use ::foo::baz::foobaz;
}

fn main() {}

2015 에디션은 use 선언이 extern 프렐류드를 참조하는 걸 허용하지 않아요. 그래서 2015에서는 use 선언에서 외부 크레이트를 참조하려면 여전히 extern crate 선언이 필요해요. 2018 에디션부터는 use 선언이 extern crate처럼 외부 크레이트 의존성을 지정할 수 있어요.

as로 이름 바꾸기

as 키워드는 가져온 개체의 이름을 바꾸는 데 써요.

#![allow(unused)]
fn main() {
// 함수 `foo`에 대한 공개되지 않은 별칭 `bar`를 만들어요.
use inner::foo as bar;

mod inner {
    pub fn foo() {}
}
}

중괄호 문법

중괄호는 경로의 마지막 세그먼트에서, 이전 세그먼트(없으면 현재 스코프)에서 여러 개체를 가져오는 데 쓸 수 있어요. 중괄호는 중첩될 수도 있어서 경로의 트리를 만들고, 각 세그먼트 묶음이 부모와 논리적으로 결합해 전체 경로를 만들어요.

#![allow(unused)]
fn main() {
// 다음에 대한 바인딩을 만들어요:
// - `std::collections::BTreeSet`
// - `std::collections::hash_map`
// - `std::collections::hash_map::HashMap`
use std::collections::{BTreeSet, hash_map::{self, HashMap}};
}

빈 중괄호는 아무것도 가져오지 않아요. 다만 앞의 경로가 접근 가능한지는 검증돼요.

2018 에디션 차이

2015 에디션에서는 경로가 크레이트 루트 기준으로 상대적이어서, use {foo, bar}; 같은 가져오기는 크레이트 루트에서 이름 foobar를 가져와요. 반면 2018부터는 그 이름들이 현재 스코프 기준으로 상대적이에요.

self 가져오기

self 키워드는 중괄호 문법 안에서 부모 개체의 바인딩을 자신의 이름으로 만들 때 쓰여요.

mod stuff {
    pub fn foo() {}
    pub fn bar() {}
}
mod example {
    // `stuff`과 `foo`에 대한 바인딩을 만들어요.
    use crate::stuff::{self, foo};
    pub fn baz() {
        foo();
        stuff::bar();
    }
}
fn main() {}

참고

self는 경로의 첫 세그먼트로도 쓸 수 있어요. 첫 세그먼트로 쓰는 것과 use 중괄호 안에서 쓰는 것은 논리적으로 같아요. 부모 세그먼트의 현재 모듈, 또는 부모 세그먼트가 없으면 현재 모듈을 뜻해요. 경로 챕터의 self 설명을 더 참고하세요.

self::가 앞에 붙은 use 경로의 마지막 세그먼트로도 나타날 수 있어요. P::self 형태의 경로는 P::{self}와 동등하고, P::self as nameP::{self as name}과 동등해요.

mod m {
    pub enum E { V1, V2 }
}
use m::self as _; // `use m::{self as _};`와 동등해요.
use m::E::self; // `use m::E::{self};`와 동등해요.
fn main() {}

참고

앞에 오는 경로에 대한 제한은 경로 챕터에서 확인할 수 있어요.

중괄호 문법 안에서 self를 쓸 때, 중괄호 묶음 앞의 경로는 모듈, 열거형, 또는 트레잇으로 해석되어야 해요.

mod m {
    pub enum E { V1, V2 }
    pub trait Tr { fn f(&self); }
}
use m::{self as _}; // OK: 모듈은 `self`의 부모가 될 수 있어요.
use m::E::{self, V1}; // OK: 열거형은 `self`의 부모가 될 수 있어요.
use m::Tr::{self}; // OK: 트레잇은 `self`의 부모가 될 수 있어요.
fn main() {}
struct S {}
use S::{self as _}; // ERROR: 구조체는 `self`의 부모가 될 수 없어요.
fn main() {}

self는 부모 개체에서 타입 네임스페이스의 바인딩만 만들어요. 예를 들어 아래에서는 foo 모듈만 가져와요.

mod bar {
    pub mod foo {}
    pub fn foo() {}
}

// 이것은 모듈 `foo`만 가져와요. 함수 `foo`는 값 네임스페이스에
// 살고 있어서 가져오지 않아요.
use bar::foo::{self};

fn main() {
    foo(); //~ ERROR `foo` is a module
}

글롭 가져오기(Glob imports)

* 문자는 use 경로의 마지막 세그먼트로 써서, 이전 세그먼트의 개체에서 가져올 수 있는 모든 개체를 가져올 수 있어요.

#![allow(unused)]
fn main() {
// `bar`에 대한 공개되지 않은 별칭을 만들어요.
use foo::*;

mod foo {
    fn i_am_private() {}
    enum Example {
        V1,
        V2,
    }
    pub fn bar() {
        // `Example` 열거형의 `V1`과 `V2`에 대한
        // 로컬 별칭을 만들어요.
        use Example::*;
        let x = V1;
    }
}
}

아이템과 이름 붙은 가져오기는 같은 네임스페이스에서 글롭 가져오기의 이름을 섀도잉할 수 있어요. 즉, 같은 네임스페이스에 이미 다른 아이템으로 정의된 이름이 있으면 글롭 가져오기가 섀도잉돼요. 아래 예시를 볼게요.

#![allow(unused)]
fn main() {
// `clashing::Foo` 튜플 구조체 생성자에 대한 바인딩을 만들지만,
// 여기 정의된 `Foo` 구조체와 충돌하므로 그 타입은 가져오지 않아요.
//
// 여기 정의 순서는 중요하지 않아요.
use clashing::*;
struct Foo {
    field: f32,
}

fn do_stuff() {
    // `clashing::Foo`의 생성자를 사용해요.
    let f1 = Foo(123);
    // 구조체 표현식은 위에서 정의한 `Foo` 구조체의 타입을 사용해요.
    let f2 = Foo { field: 1.0 };
    // `Bar`도 글롭 가져오기 덕분에 스코프에 있어요.
    let z = Bar {};
}

mod clashing {
    pub struct Foo(pub i32);
    pub struct Bar {}
}
}

참고

섀도잉이 허용되지 않는 영역은 이름 해석 모호성 문서를 참고하세요.

제한도 있어요. *는 첫 번째나 중간 세그먼트로는 쓸 수 없어요. 그리고 use self::*;처럼 모듈 내용을 자기 자신으로 가져오는 데 *를 쓸 수 없어요.

2018 에디션 차이

2015 에디션에서는 경로가 크레이트 루트 기준으로 상대적이어서, use *; 같은 가져오기가 유효하고 크레이트 루트에서 모든 것을 가져오는 뜻이에요. 다만 이것은 크레이트 루트 자체에서는 사용할 수 없어요.

밑줄 가져오기(Underscore imports)

use path as _ 형태로 밑줄을 써서 이름에 바인딩하지 않고 아이템을 가져올 수 있어요. 이는 트레잇 심볼이 다른 심볼과 충돌할 수 있을 때처럼, 트레잇의 심볼을 가져오지 않고 그 메서드를 쓸 수 있게 하려고 트레잇을 가져올 때 특히 유용해요. 또 다른 예로는 이름을 가져오지 않고 외부 크레이트를 링크하는 경우예요.

mod foo {
    pub trait Zoo {
        fn zoo(&self) {}
    }

    impl<T> Zoo for T {}
}

use self::foo::Zoo as _;
struct Zoo;  // 밑줄 가져오기가 이 아이템과의 이름 충돌을 피해줘요.

fn main() {
    let z = Zoo;
    z.zoo();
}

별표 글롭 가져오기는 _로 가져온 아이템을 이름 붙일 수 없는 형태로 가져와요.

특별한, 이름 붙일 수 없는 심볼은 매크로 확장 후에 만들어져서, 매크로가 _ 가져오기에 대한 참조를 안전하게 여러 번 만들어낼 수 있어요. 아래 코드는 오류를 내지 않아요.

#![allow(unused)]
fn main() {
macro_rules! m {
    ($item: item) => { $item $item }
}

m!(use std as _;);
// 이렇게 확장돼요:
// use std as _;
// use std as _;
}

제한 사항(Restrictions)

유효한 use 선언을 위한 제한 규칙이 몇 가지 있어요.

  • crate로 현재 크레이트를 가져올 때는 바인딩 이름을 정의하려고 as를 써야 해요.
#![allow(unused)]
fn main() {
use crate as root;
use crate::{self as root2};

// 허용되지 않아요:
// use crate;
// use crate::{self};
}
  • 매크로 변환기(transcriber)에서 $crate로 현재 크레이트를 가져올 때도 바인딩 이름을 정의하려고 as를 써야 해요.
#![allow(unused)]
fn main() {
macro_rules! import_crate_root {
    () => {
        use $crate as my_crate;
        use $crate::{self as my_crate2};
    };
}
}
  • self로 현재 모듈을 가져올 때는 바인딩 이름을 정의하려고 as를 써야 해요.
#![allow(unused)]
fn main() {
use {self as this_module};
use self as this_module2;
use self::{self as this_module3};

// 허용되지 않아요:
// use {self};
// use self;
// use self::{self};
}
  • super로 부모 모듈을 가져올 때도 바인딩 이름을 정의하려고 as를 써야 해요.
#![allow(unused)]
fn main() {
mod a {
    mod b {
        use super as parent;
        use super::{self as parent2};
        use self::super as parent3;
        use super::super as grandparent;
        use super::super::{self as grandparent2};

        // 허용되지 않아요:
        // use super;
        // use super::{self};
        // use self::super;
        // use super::super;
        // use super::super::{self};
    }
}
}
  • extern 프렐류드로 ::를 가져올 수 없어요.
#![allow(unused)]
fn main() {
use ::{self as root}; //~ Error
}

2018 에디션 차이

2015 에디션에서는 :: 접두사가 크레이트 루트를 가리켜서, use ::{self as root};use crate::{self as root};와 같으므로 허용됐어요. 2018 에디션부터 :: 접두사는 extern 프렐류드를 가리키는데, 이는 직접 가져올 수 없어요.

#![allow(unused)]
fn main() {
use ::{self as root}; //~ Ok
}
  • 어떤 아이템 정의와 마찬가지로, use 가져오기는 모듈이나 블록에서 같은 네임스페이스에 같은 이름의 중복 바인딩을 만들 수 없어요.
  • use 경로는 타입 별칭을 통해 열거형 변형을 참조할 수 없어요.
#![allow(unused)]
fn main() {
enum MyEnum {
  MyVariant
}
type TypeAlias = MyEnum;

use MyEnum::MyVariant; //~ OK
use TypeAlias::MyVariant; //~ ERROR
}

더 알아보기

  • 경로와 self, super, crate, $crate
  • 가시성과 프라이버시, 재수출
  • 이름 해석과 프렐류드
  • 파생 매크로와 macro_rules