Rust의 모듈 시스템

러스트의 모듈 시스템은 아래 4가지를 말합니다.

  • 패키지(Packages) : cargo에서 제공하는 기능으로, crate를 빌드하고 생성할 수 있습니다.
  • 크레이트(Crates) : 라이브러리 또는 바이너리를 생성하는 모듈 트리(a tree of modules)입니다.
  • moduse: 코드 안에서 다른 모듈들을 구성하고, 불러오거나 다른 모듈에 노출할 지 여부(private or public)를 결정합니다.
  • 경로: 모듈에서 특정 요소(함수, 구조체, 변수 등)를 찾기 위한 방법

패키지

cargo.toml 파일

하나의 패키지에는 단 하나의 라이브러리 크레이트만 포함할 수 있습니다. 하지만 바이너리 크레이트는 여러 개를 넣을 수 있습니다.

크레이트

바이너리 크레이트

main.rs

cargo new

컴파일되어 바이너리 파일을 생성하는 크레이트입니다.

라이브러리 크레이트

lib.rs

cargo new --lib

컴파일되지 않기 때문에 바이너리를 생성하지 않습니다. 다른 크레이트나 패키지에서 코드를 참조할 수 있도록 제공되는 크레이트입니다.

크레이트 루트

크레이트 루트란 컴파일 엔트리포인트를 의미합니다. 바이너리 크레이트는 src/main.rs 파일이, 라이브러리 크레이트는 src/lib.rs 파일이 크레이트 루트가 됩니다.

private vs public

러스트의 모든 모듈과 객체는 기본적으로 private입니다. 즉 모듈 외부에서 해당 모듈이나 객체에 접근이 불가능합니다. 따라서 외부에서 모듈에 접근하거나 모듈 내부의 객체에 접근을 허용하려면 pub 키워드를 사용해야 합니다.

pub mod {

}

pub fn {

}

pub struct {

}

pub static

usemod

use 키워드는 특정 경로를 현재 스코프로 가져오는 역할을 합니다. 주의해야 하는 점은 경로는 항상 크레이트 루트로부터 시작된다는 점입니다.

mod 키워드는 해당 모듈을 사용하겠다고 선언하는 역할입니다. 예를 들어 mod new_module이 사용되면, 컴파일러는 아래 위치에서 해당 모듈을 찾아봅니다.

  1. mod new_module 다음에 해당 모듈의 정의가 나와야 합니다.

    mod new_module {
      fn new_func() {
        ...
      }
    
      ...
    }
  2. src/new_module.rs 파일을 찾아봅니다.

  3. src/new_module 폴더에서 mod.rs 파일을 찾아봅니다.

    pub mod new_module;

마찬가지로 서브모듈도 정의가 가능합니다. 크레이트 루트가 아닌 모듈에서 선언되는 모듈이 서브모듈이 되며, 해당 서브모듈을 컴파일러가 찾는 규칙은 위와 동일합니다.

특정 모듈에 대한 접근은 크레이트 루트를 기준으로 절대경로를 사용하면 됩니다. 예를 들어 코드 어디에서라도 다음과 같이 모듈에 접근이 가능합니다.

// src/new_module.rs -> MyType
use crate::new_module::MyType

상대 경로를 사용할 때도 있는데, 이 경우에는 selfsuper 키워드를 사용합니다.

self 는 struct 자기 자신을 의미합니다.

mod mod2 {
    fn func() {
        println!("mod2::func()");
    }

    mod mod1 {
        pub fn func() {
            println!("mod2::mod1::func()");
        }
    }

    pub fn dummy() {
        func();
        self::func();
        mod1::func();
        self::mod1::func();
    }
}

fn main() {
    mod2::dummy();
}

실행 결과

mod2::func()
mod2::func()
mod2::mod1::func()
mod2::mod1::func()

super는 현재 모듈의 상위 모듈을 의미합니다.

mod mod1 {
    pub fn dummy() {
        println!("Hello, world!");
    }
}

mod mod2 {
    // use crate::mod1;
    use super::mod1;

    pub fn dummy() {
        mod1::dummy();
    }
}

fn main() {
    mod2::dummy();
}