오늘은 Rust의 crate, super, pub에 대해 알아보고자 합니다.
패키지와 크레이트
크레이트(crate)는 rust가 컴파일 한 차례에 고려하는 가장 작은 코드 단위입니다.
cargo 대신 rustc를 실행하여 단일 소스 코드 파일을 넘겨주더라도, 컴파일러는 그 파일이 크레이트라고 생각합니다.
크레이트는 바이너리일 수도 있고, 라이브러리일 수도 있습니다.
바이너리 크레이트(binary crate)는 커맨드 라인 프로그램이나 서버처럼 실행 가능한 실행파일로 컴파일할 수 있는 프로그램입니다.
바이너리 크레이트는 실행파일이 실행되면 무슨 일이 일어나는지를 정의한 main 함수를 포함하고 있어야 합니다.
라이브러리 크레이트(library crate)는 main 함수를 가지고 있지 않고 실행파일 형태로 컴파일되지 않습니다.
그 대신, 여러 프로젝트에서 공용될 의도로 만들어진 기능들이 정의되어 있습니다.
크레이트 루트(crate root)는 rust 컴파일러가 컴파일을 시작하는 소스 파일이고, 크레이트의 루트 모듈을 구성합니다.
패키지(package)는 일련의 기능을 제공하는 하나 이상의 크레이트로 구성된 번들입니다.
패키지에는 이 크레이트들을 빌드하는 법이 설명된 Cargo.toml파일이 포함되어 있습니다.
패키지에는 여러 개의 바이너리 크레이트가 원하는 만큼 포함될 수 있지만, 라이브러리 크레이트는 하나만 넣을 수 있습니다.
cargo new를 실행한 후, ls 명령어로 살펴보게 되면, 프로젝트 디렉터리에는 Cargo.toml파일이 패키지를 만들어 주고, 패키지명과 같은 이름의 바이너리 크레이트는 src/main.rs가 크레이트 루트라는 관례를 준수합니다.
마찬가지로, 패키지 디렉터리에 src/lib.rs 파일이 존재할 경우, Cargo는 해당 패키지가 패키지명과 같은 이름의 라이브러리 크레이트를 포함하고 있다고 판단합니다.
모듈을 정의하여 스코프 및 공개 여부 제어하기
모듈, 아이템의 이름을 지정하는 경로(path), 스코프에 경로를 가져오는 use 키워드, 아이템을 공개하는 데 사용하는 pub 키워드가 있습니다.
모듈 치트 시트
- 크레이트 루트부터 시작 : 크레이트를 컴파일할 때 컴파일러는 먼저 크레이트 루트 파일을 봅니다.
- 모듈 선언 : 크레이트 루트 파일에는 새로운 모듈을 선언할 수 있습니다.
- 서브모듈 선언 : 크레이트 루트가 아닌 다른 파일에서는 서브모듈(submodule)을 선언할 수 있습니다.
- 모듈 내 코드로의 경로 : 모듈이 크레이트의 일부로서 구성되면, 공개 규칙이 허용하느 한도 내에서라면 해당 코드의 경로를 사용하여 동일한 크레이트의 어디에서든 이 모듈의 코드를 참조할 수 있게 됩니다.
- 비공개 vs 공개 : 모듈 내의 코드는 기본적으로 부모 모듈에세 비공개(private) 입니다. 모듈을 공개(public)로 만들려면, mod 대신 pub mod를 써서 선언하세요.
- use 키워드 : 어떤 스코프 내에서 use 키워드는 긴 경로의 반복을 줄이기 위한 어떤 아이템으로의 단축경로를 만들어 줍니다.
use crate::garden::vegetables::Asparagus;
pub mod garden;
fn main() {
let plant = Asparagus {};
println!("I'm growing {:?}!", plant);
}
pub mod garden; 라인이 컴파일러에게 src/garden.rs에 있는 코드를 포함할 것을 알려줍니다.
모듈로 관련된 코드 묶기
모듈은 크레이트의 코드를 읽기 쉽고 재사용하기도 쉽게끔 구조화 할 수 있게 해줍니다.
모듈 내의 코드는 기본적으로 비공개이므로, 모듈은 아이템의 공개 여부(privacy)를 제어하도록 해주기도 합니다.
모듈과 모듈 내 아이템을 선택적으로 공개할 수 있는데, 이렇게 하여 외부의 코드가 모듈 및 아이템을 의존하고 사용할 수 있도록 노출해 줍니다.
src/main.rs와 src/lib.rs가 크레이트 루트라는 이름을 갖게 된 이유는 모듈 트리(module tree)라고 불리는 크레이트 모듈 구조에서 최상위에 crate라는 이름을 갖는 일종의 모듈로 형성되기 때문입니다.
crate
└── front_of_house
├── hosting
│ ├── add_to_waitlist
│ └── seat_at_table
└── serving
├── take_order
├── serve_order
└── take_payment
이 트리는 어떤 모듈이 서로 형제(sibling) 관계에 있는지 나타내기도 하는데, 이는 동일한 모듈 내에 정의되어 있음을 말합니다.
모듈 A가 모듈 B 안에 있으면, 모듈 A는 모듈 B의 자식이며, 모듈 B는 모듈 A의 부모라고 말합니다.
경로를 사용하여 모듈 트리의 아이템 참조하기
rust 모듈 트리에서 아이템을 찾는 방법은, 파일 시스템에서 경로를 사용하는 방법과 동일하게, 2가지 형태가 존재합니다.
- 절대 경로(absolute path)는 크레이트 루트로부터 시작되는 전체 경로입니다.
- 상대 경로(relative path)는 현재의 모듈을 시작점으로 하여 self, super 혹은 현재 모듈 내의 식별자를 사용합니다.
절대 경로, 상대 경로 뒤에는 ::으로 구분된 식별자가 하나 이상 따라옵니다.
상대 경로, 절대 경로 중 무엇을 사용할지는 프로젝트에 따라, 그리고 아이템을 정의하는 코드와 아이템을 사용하는 코드를 분리하고 싶은지, 혹은 같이 두고 싶은지에 따라 여러분이 결정해야 할 사항입니다.
일반적으로 선호하는 경로는 절대 경로입니다.
아이템을 정의하는 코드와 호출하는 코드는 분리되어 있을 가능성이 높기 떄문입니다.
부모 묘듈 내 아이템은 자식 모듈 내 비공개 아이템을 사용할 수 없지만, 자식 모듈 내 아이템은 부모 모듈 내 아이템을 사용할 수 있습니다.
이유는, 자식 모듈의 세부 구현은 감싸져서 숨겨져 있지만, 자식 모듈 내에서는 자신이 정의된 컨텍스트를 볼 수 있기 때문입니다.
rust 모듈 시스템은 내부의 세부 구현을 기본적으로 숨기도록 되어 있습니다.
여러분은 외부 코드의 동작을 망가뜨릴 걱정 없이 수정할 수 있는 코드가 어느 부분인지 알 수 있습니다.
그렇지만 rust에서는 pub 키워드를 사용하여 자식 모듈의 내부 구성 요소를 공개(public) 함으로써 외부의 상위 모듈로 노출할 방법을 제공합니다.
pub 키워드로 경로 노출하기
모듈의 pub 키워드는 상위 모듈이 해당 모듈을 가리킬 수 있도록 할 뿐, 그 내부 코드에 접근하도록 하는 것은 아닙니다.
모듈은 단순한 컨테이너이기 때문에 모듈을 공개하는 것만으로 할 수 있는 것은 별로 없으며, 여기에 더해서 모듈이 가지고 있는 아이템도 마찬가지로 공개해야 합니다.
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
pub fn eat_at_restaurant() {
// 절대 경로
crate::front_of_house::hosting::add_to_waitlist();
// 상대 경로
front_of_house::hosting::add_to_waitlist();
}
다른 프로젝트에서 여러분의 코드를 사용할 수 있도록 라이브러리 크레이트를 공유할 계획이라면, 여러분의 공개 API는 크레이트의 사용자가 코드와 상호 작용하는 방법을 결정하는 계약입니다.
super로 시작하는 상대 경로
super로 시작하면 현재 모듈 혹은 크레이트 루트 대신 자기 부모 모듈로부터 시작되는 상대 경로를 만들 수 있습니다.
super를 사용하면 부모 모듈에 위치하고 있음을 알고 있는 아이템을 참조하도록 해주고, 이는 모듈이 부모 모듈과 밀접한 관련이 있지만 부모 모듈은 나중에 모듈 트리의 다른 어딘가로 옮겨질지도 모르는 경우 모듈 트리의 재조정을 편하게 만들어줍니다.
fn deliver_order() {}
mod back_of_house {
fn fix_incorrect_order() {
cook_order();
super::deliver_order();
}
fn cook_order() {}
}
구조체, 열거형을 공개하기
구조체 정의에 pub를 쓰면 구조체는 공개되지만, 구조체의 필드는 비공개로 유지되어 공개 여부는 각 필드마다 정할 수 있습니다.
mod back_of_house {
pub struct Breakfast {
pub toast: String,
seasonal_fruit: String,
}
impl Breakfast {
pub fn summer(toast: &str) -> Breakfast {
Breakfast {
toast: String::from(toast),
seasonal_fruit: String::from("peaches"),
}
}
}
}
열거형을 공개로 지정할 경우 모든 배리언트가 공개됩니다.
mod back_of_house {
pub enum Appetizer {
Soup,
Salad,
}
}
pub fn eat_at_restaurant() {
let order1 = back_of_house::Appetizer::Soup;
let order2 = back_of_house::Appetizer::Salad;
}
정리
- 크레이트의 종류로는 라이브러리 크레이트와 바이너리 크레이트가 존재한다.
- 어떤 스코프 내에서 use 키워드는 긴 경로의 반복을 줄이기 위한 어떤 아이템으로의 단축경로를 만들어 줍니다.
- 일반적인 라이브러리 크레이트를 불러와서 사용할 경우 private으로 지정되고, pub을 붙여 사용할 경우 public으로 지정된다.
- 구조체는 구조체명에 pub를 붙이면 해당 구조체만 public이 되고 필드는 사용할 수 없어, 모든 필드에 pub를 붙여야 한다.
- 열거형의 경우 이름에 pub를 붙이면 모든 배리언트가 public이 된다
'Rust' 카테고리의 다른 글
Rust 설치부터 실행까지 (use, pub use, as, mod) - 16 (0) | 2025.01.15 |
---|---|
Rust 설치부터 실행까지 (match, if let) - 14 (0) | 2025.01.12 |
Rust 설치부터 실행까지 (열거형, Option) - 13 (0) | 2025.01.12 |
Rust 설치부터 실행까지 (메서드) - 12 (0) | 2025.01.09 |
Rust 설치부터 실행까지 (구조체, 디버깅) - 11 (0) | 2025.01.07 |
댓글