모듈 시스템
러스트에는 코드 조직화에 필요한 여러 기능들이 있는데, 어떤 세부 정보를 외부에 노출할지, 비공개로 둘지, 프로그램 스코프 내 어떤 이름이 있는지 등을 제공. 모듈 시스템에는 패키지, 크레이트, 모듈 및 use, 경로와 같은 기능들이 포함됨
- 패키지
크레이트를 빌드, 테스트, 공유
- 크레이트
라이브러리나 실행 가능한 모듈로 구성된 트리 구조
- 모듈 및 use
구조, 스코프를 제어하고 조직 세부 경로를 감추는데 사용
- 경로
구조체, 함수 모듈 등의 이름을 지정
크레이트 (crate)
러스트가 한 번의 컴파일 시에 고려하는 가장 작은 코드 단위
크레이트는 여러 모듈을 담을 수 있고, 모듈은 크레이트와 함께 컴파일되는 다른 파일들에 정의되어 있을 수 있음
크레이트는 바이너리일 수도 있고 라이브러리 일수도 있음
- 바이너리 크레이트
실행 가능한 실행 파일로 컴파일 할 수 있는 프로그램
바이너리 크레이트는 실행 파일이 실행되면 무슨 일이 일어나는지를 정의한 main 함수를 포함하고 있어야 함
- 라이브러리 크레이트
main 함수를 가지고 있지 않고 실행 파일 형태로 컴파일 되지 않는 크레이트
대신 여러 프로젝트에서 공용될 의도로 만들어진 기능들이 정의되어 있음
흔히 크레이트라고 하면 대부분은 라이브러리 크레이트를 의미
- 크레이트 루트
러스트 컴파일러가 컴파일을 시작하는 소스 파일로 크레이트의 루트 모듈을 구성
패키지
일련의 기능을 제공하는 하나 이상의 크레이트로 구성된 번들
패키지에는 이 크레이트들을 빌드하는 방법이 설명된 Cargo.toml 파일이 포함되어 있음
패키지에는 여러 개의 바이너리 크레이트가 포함될 수 있지만 라이브러리 크레이트는 하나만 넣을 수있음
패키지에는 적어도 하나 이상의 크레이트가 포함되어야하며, 이는 라이브러리든 바이너리든 상관 없음
- Cargo.toml
cargo new 명령을 실행 후 카고가 만드는 파일로 이 파일의 명세를 통해 패키지가 생성됨
- src/main.rs
바이너리 크레이트의 크레이트 루트
- src/lib.rs
라이브러리 크레이트의 크레이트 루트
빈 디렉터리에 cargo new --lib 라이브러리명 명령으로 생성
여기에 모듈 트리가 정의되어야함
- src/bin
여러 바이너리 크레이트를 패키지에 포함하는 디렉터리
카고는 라이브러리 혹은 바이너리를 빌드할 때 이 크레이트 루트 파일을 rustc에 전달
모듈
크레이트의 코드를 읽기 쉽고 재사용하기도 쉽게 구조화 할 수 있게 해주는 컨테이너
모듈 내의 코드를 기본적으로 비공개이나, 모듈 내 아이템의 공개 여부를 결정할 수 있음
모듈은 src 디렉터리 내에 rs 파일로 작성할 수 있으며 서브 모듈까지
- main.rs에서 모듈 사용
use crate::XX::YY::ZZ; # 모듈 내 타입을 단축하여 참조
pub mod XX; // 모듈 공개 선언 (모듈 내의 코드는 기본적으로 부모 모듈에게 비공개)
fn main() {
let a = ZZ {};
println!("{::?}", a);
}
- 공개여부 (privacy)
모듈 내의 아이템을 외부에서 사용할 수 있는지에 대한 여부
러스트에서는 함수, 메서드, 구조체, 열거형, 모듈, 상수 등 모든 아이템이 기본적으로 부모 모듈에 대해 비공개
부모 모듈안에 있는 아이템은 자식 모듈 내 비공개 아이템을 사용할 수 없지만,
자식 모듈 내 아이템은 조상 모듈 내 아이템을 사용 가능
러스트 모듈 시스템은 내부의 세부 구현을 기본적으로 숨기도록 되어있으나 pub 키워드를 통해 자식 모듈의 내부 구성 요소를 공개함으로써 외부의 상위 모듈로 노출할 방법을 제공
그러나 모듈을 공개한다고하여 내용까지 공개되지는 않기 때문에 모듈 내에 공개할 아이템에 대해 일일이 pub 키워드를 작성해야함
비공개 규칙은 구조체, 열거형, 함수, 메서드, 모듈 모두에게 적용됨
경로
러스트 모듈 트리에서 아이템을 찾기 위한 방법. ::으로 구분
- 절대 경로 (absolute path)
크레이트 루트로부터 시작되는 전체 경로
외부 크레이트로부터의 코드에 대해서는 해당 크레이트 이름으로 절대 경로가 시작
현재의 크레이트로부터의 코드에 대해서는 crate 리터럴로 부터 시작됨
crate 키워드로 시작하면 절대 경로
- 상대 경로 (relative path)
현재의 모듈을 시작점으로 하여 self, super 혹은 현재 모듈 내의 식별자를 사용
모듈 이름으로 시작하면 상대 경로임
// src/lib.rs
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();
}
super로 시작하는 상대경로
super로 시작하면 현재 모듈 혹은 크레이트 루트 대신 자기 부모 모듈부터 시작되는 상대 경로를 만들 수 있음
이는 파일 시스템 경로에서 ..으로 시작하는 것과 동일
super 키워드를 사용하면 부모 모듈에 위치한 아이템을 참조할 수 있음
// src/lib.rs
fn deliver_order() {}
mod back_of_house {
fn fix_incorrect_order() {
cook_order();
super::deliver_order();
}
fn cook_order() {}
}
구조체와 열거형 공개
pub 키워드로 구조체와 열거형을 공개할 수 있지만 내부의 필드는 비공개로 유지됨
구조체의 경우 각 필드마다 공개 여부를 설정할 수 있지만, 열거형은 배리언트가 공개되지 않으면 의미가 없기에 모든 필드가 공개됨
// src/lib.rs
mod back_of_house {
pub struct Breakfast {
pub toast: String,
seasonal_fruit: String,
}
impl Breakfast {
pub fn summer(toast: &str) -> Breakfase {
Breakfast {
toast: String::from(toast),
seasonal_fruit: String::from("peasches"),
}
}
}
}
pub fn eat_at_restaurant() {
let mut meal = back_of_house::Breakfask::summer("Rye");
meal.toast = String::from("wheat");
// 구조체 내 pub 필드가 아니기 때문에 주석 해제 시 오류 발생
// meal.seasonal_fruit = String::from("apple");
}
use 키워드
- 경로를 스코프 안으로 가져오기
use 키워드를 사용하여 어떤 경로의 단축경로를 만들 수 있음
다만, 단축경로는 use가 사용된 특정된 스코프에서만 만들어지기 때문에 새로운 자식 모듈 등 스코프가 나눠지는 경우에는 super 키워드를 통해 부모 모듈로의 단축 경로를 참조해야함
// src/lib.rs
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
use crate::front_of_house::hosting;
// 가능은하지만 사용 시 모듈 특정이 안되기 때문에 보편적으로는 사용하지 않는 형태
// use crate::front_of_house::hosting::add_to_waitlist;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
mod customer {
pub fn eat() {
super::hosting::add_to_waitlist(); // 부모 모듈의 단축 경로를 참조
}
}
as 키워드로 새로운 이름 제공
// src/lib.rs
use std::fmt::Result;
use std::io::Result as IoResult;
fn function1() -> Result { Result }
fn function2() -> IoResult<()> { IoResult }
pub use로 다시 내보내기 (re-exporting)
use 키워드로 이름을 가져올 경우, 해당 이름은 새 위치의 스코프에서 비공개가 됨
pub use 두 키워드를 같이 사용하면 우리 코드를 호출하는 코드가 해당 스코프에 정의된 것처럼 해당 이름을 참조할 수 있음
이 기법은 아이템을 스코프로 가져오는 동시에 다른 곳에서 아이템을 가져갈 수 있도록 해줌
// src/lib.rs
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
pub use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
외부 패키지 사용하기
Cargo.toml의 dependency에 `패키지명 = "버전"`을 추가하면 구현하고 있는 패키지의 스코프에서 use 키워드를 통해 가져와서 쓸 수 있음
동일한 크레이트 내 여러 아이템 가져오기
use의 중첩 경로를 통해 동일한 크레이트나 모듈에서 여러 아이템을 가져올 수 있음
// 중첩 경로
use std::{cmp::Ordering, io};
use std::io{self, Write}; // self는 use std::io와 같음
// 글롭(glob) 연산자
use std::collections::*; // 경로 내 정의된 모든 아이템을 가져옴
// 단, 코드에 사용된 이름이 어느 곳에 정의되어 있는지 파악하기 어려우므로 주의
별개의 파일로 모듈 분리
크레이트 루트 파일에 모든 모듈이 정의되는 형태 대신 모듈들을 파일로 분리하면 바이너리 크레이트에서도 작동이 가능해진다
// src/lib.rs
mod front_of_house;
pub use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
- 모듈을 단일 파일로 분리
// src/front_of_house.rs
pub mod hosting {
pub fn add_to_wailist() {}
}
- 모듈을 디렉터리로 분리
// src/front_of_house.rs
pub mod hosting;
// src/front_of_house/hosting.rs
pub fn add_to_wait_list() {}
'Programming > Rust' 카테고리의 다른 글
| 러스트 에러 처리 (0) | 2025.09.08 |
|---|---|
| 러스트 컬렉션 (collection) (1) | 2025.09.02 |
| 러스트의 열거형과 패턴 매칭 (1) | 2025.08.24 |
| 러스트의 구조체에 대해 알아보자 (3) | 2025.08.18 |
| 러스트 소유권 이해하기 (7) | 2025.08.13 |
