[Rust]

[Rust] 패키지, 크레이트, 모듈로 프로젝트 관리

극꼼 2023. 8. 11. 03:18
반응형


프로젝트 규모가 커지면 코드를 여러 모듈, 파일로 나눠서 관리해야 합니다. 한 패키지는 여러 개의 바이너리 크레이트와 라이브러리 크레이트를 포함할 수 있고, 커진 프로젝트의 각 부분을 크레이트로 나눠서 외부 라이브러리처럼 쓸 수 있습니다. 

러스트에서 코득 조직화에 필요로하는 모듈 시스템에는 다음과 같은 기능들이 있습니다.

  • 패키지 : 크레이트를 빌드, 테스트, 공유하는 데 사용하는 카고 기능
  • 크레이트 : 라이브러리나 실행 가능한 모듈로 구성된 트리 구조
  • 모듈과 use : organization, scope를 제어하고, 조직 세부 경로를 감추는 데 사용
  • 경로 : 구조체, 함수, 모듈 등의 이름을 지정

<패키지와 크레이트>

- 크레이트(crate) : 러스트가 컴파일 한 차례에 고려하는 가장 작은 코드 단위입니다. 크레이트는 여러 모듈을 담을 수 있고, 모듈은 이 크레이트와 함께 컴파일되는 다른 파일들에 정의될 수 있습니다.

크레이트에는 바이너리와 라이브러리가 있습니다.

  • 바이너리 크레이트(binary crate) : 실행파일로 컴파일할 수 있는 프로그램입니다. 바이너리 크레이트는 main 함수를 포함하고 있어야 합니다.
  • 라이브러리 크레이트(library crate) : main 함수를 가지고 있지 않고, 실행 파일 형태로 컴파일되지 않습니다. 대신 여러 프로젝트에서 공용으로 사용될 기능들이 정의되어 있습니다.

 

- 크레이트 루트(crate root) : 러스트 컴파일러가 컴파일러를 시작하는 소스 파일로, 크레이트의 루트 모듈을 구성합니다.

 

- 패키지(package) : 일련의 기능을 제공하는 하나 이상의 크레이트로 구성된 번들입니다. 패키지에는 해당 크레이트들을 빌드하는 법이 설명된 Cargo.toml 파일이 포함되어 있습니다. cargo도 커맨드 라인 도구의 바이너리 크레이트가 포함된 패키지입니다.


<모듈>

- 크레이트를 컴파일 할 때 컴파일러는 크레이트 루트를 먼저 봅니다. (라이브러리 크레이트의 경우 src/lib.rs, 바이너리 크레이트의 경우 src/main.rs)

 

- 모듈 : 크레이트 루트 파일에는 새로운 모듈을 선언할 수 있습니다.

mod garden; // garden이라는 모듈

 

- 서브모듈 : 크레이트 루트 외의 다른 파일에서 서브모듈을 선언할 수 있습니다.

 

- path : 모듈이 크레이트의 일부로서 공개되면, 공개 규칙이 허용하는 한도 내에서 해당 코드의 path를 사용해서 동일한 크레이트 어디서든 이 모듈의 코드를 참조할 수 있게 합니다.

crate::garden::vegetables::Asparagus // garden 모듈의 vegetables 모듈 안에 있는 Asparagus 타입

 

- pub : 모듈 내의 코드는 기본적으로 private입니다. 따라서 공개로 만들기 위해서는 mod 대신 pub mod로 선언해야 합니다.

 

- use : 어떤 스코프 내의 use 키워드는 긴 경로의 반복을 줄이기 위해 쓰입니다.

use crate::garden::vegetables::Asparagus;
// Asparagus만 작성하면 해당 타입 사용 가능

 

- 모듈은 크레이트의 코드를 읽고 재사용하기 쉽게 구조화할 수 있습니다. 다음은 예시를 위해 호스트와 서버 업무를 각각 다른 모듈로 나눈 코드입니다. hosting, serving은 front_of_house 모듈 내에 정의된 형제이고, front_of_house는 부모 모듈입니다.

mod front_of_house {
    pub mod hosting { 
        pub fn add_to_waitlist() {}
        pub fn seat_at_table() {}
    }

    pub mod serving {
        fn take_order() {}
        fn serve_order() {}
        fn take_payment() {}
    }
}

파일 시스템의 디렉토리처럼 모듈도 코드를 조직화합니다.


<path>

모듈 트리에서 아이템을 찾으려면 그 아이템의 경로를 알아야합니다.

경로는 다음과 같은 두 가지 형태가 있습니다.

  • 절대 경로(absolute path) : 크레이트 루트로부터 시작되는 경로.
  • 상대 경로(relative path) : 현재 모듈을 시작점으로 하는 경로. self, super, 현재 모듈 내의 식별자를 사용합니다.

절대 경로와 상대 경로 뒤에는 :: 으로 구분된 식별자가 하나 이상 붙습니다. 위의 예시에서 정의했던 front_of_house 모듈을 예시로 살펴보겠습니다. 해당 절대 경로를 front_of_house 모듈 밖에서 사용할 경우 front_of_house 모듈은 public이 아니기 때문에 에러가 발생합니다.

mod front_of_house { ... }

pub fn eat_at_restaurant() {
    // 절대 경로
    crate::front_of_house::hosting::add_to_waitlist();

    // 상대 경로
    front_of_house::hosting::add_to_waitlist();
}

아이템을 호출하는 코드와 정의하는 코드는 분리되어 있을 가능서이 높아 일반적으로는 절대 경로가 더 선호됩니다.

 

Tip) 모듈 트리는 src/lib.rs(라이브러리 크레이트의 루트) 내에 정의되어야 바이너리 크레이트 내에서 패키지 이름으로 시작하는 경로를 사용해 모든 public 아이템을 사용할 수 있습니다.

 

- super : super로 시작하면 현재 모듈 또는 크레이트 루트 대신, 본인의 부모 모듈로부터 시작되는 상대 경로를 만들 수 있습니다. 다음 코드의 super::deliver_order(); 은 back_of_house 모듈의 부모 모듈에서 경로를 시작합니다. 

fn deliver_order() {}

mod back_of_house {
    fn fix_incorrect_order() {
        cook_order();
        super::deliver_order();
    }

    fn cook_order() {}
}

 

- 열거형의 경우, variant는 기본적으로 공개

- 구조체는 pub을 명시하지 않으면 비공개


<use>

: 함수 호출을 할 때마다 경로를 작성하지 않고, use를 사용해 단축 경로를 만듭니다. 단, use가 사용된 특정 스코프 내에서만 단축 경로가 만들어집니다. 지켜지지 않을 경우 다음과 같은 에러가 나옵니다 : error[E0433]: failed to resolve: use of undeclared crate or module `hosting`

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

...

use crate::front_of_house::hosting; // hosting의 단축 경로

pub fn eat_at_restaurant() {
    hosting::add_to_waitlist();
}

mod customer {
    pub fn eat_at_restaurant() {
        hosting::add_to_waitlist(); // use 구문과 다른 스코프가 되어 컴파일되지 않음
    }
}

 

- 이름이 같은 두 타입을 동일한 스코프에 가져오기 위해서는 반드시 다음과 같이 부모 모듈을 명시해주어야 합니다.

use std::fmt;
use std::io;

fn function1() -> fmt::Result { }

fn function2() -> io::Result<()> { }

 

as 키워드로 다음과 같이 하나의 타입 이름을 바꿔줄 수도 있습니다.

use std::fmt::Result;
use std::io::Result as IoResult;

fn function1() -> Result {
    // --생략--
}

fn function2() -> IoResult<()> {
    // --생략--
}

 

- 외부 패키지

use rand::Rng;

fn main() {
	let num = rand::thread_rng().gen_range(0..10); //0~9 사이의 숫자를 랜덤으로 픽
}

 

- 중첩 경로

use std::cmp::Ordering;
use std::io;
// 위와 같은 코드를 아래와 같은 중첩 경로로 적을 수 있습니다.
use std::{cmp::Ordering, io};

 

- 글롭 연산자 : 경로에 글롭(glob. *)을 붙이면 경로 안에 정의된 모든 pub 아이템을 가져올 수 있습니다.

use std::collections::*;

요약 참조 링크 : https://rust-kr.github.io/doc.rust-kr.org/ch07-00-managing-growing-projects-with-packages-crates-and-modules.html

반응형

'[Rust]' 카테고리의 다른 글

[Rust] match 연산자  (0) 2023.08.15
[Rust] 컬렉션(Collection)  (0) 2023.08.14
[Rust] 열거형(enum)  (0) 2023.08.01
[Rust] 구조체 - 메서드  (0) 2023.07.30
[Rust] 데이터 구조체  (0) 2023.07.29