- workspace
- 어떤 세부 정보를 외부에 노출할지, 비공개로 둘지, 프로그램의 스코프 내 어떤 이름이 있는지 등 다양한 기능이 있음. 이를 통틀어
모듈 시스템
이라 하며, 다음 기능들이 포함됩니다:
- 패키지:
- 크레이트들을
빌드하고, 테스트하고, 공유
하는 데 사용하는 카고 기능
- 크레이트:
- 컴파일 한 차례에 고려하는 가장 작은 코드 단위
- 여러 모듈을 품고 있음
- 라이브러리나 실행 가능한 모듈로 구성된 트리 구조
- 모듈:
- 크레이트의 코드를 -> 읽기 쉽고 재사용하기도 쉽게끔 구조화를 할 수 있게 해 줌
- 구조, 스코프를 제어하고,
조직 세부 경로를 감추는 데 사용
- namespace 관리를 위한 기본 단위
- 공개/비공개 조절 가능
- 한 패키지에는 여러 개의
바이너리 크레이트
와 (원할 경우) 하나의 라이브러리 크레이트가 포함
될 수 있으므로,
- 커진 프로젝트의 각 부분을 크레이트로 나눠서 외부 라이브러리처럼 쓸 수 있습니다.
- TODO: 상호연관된 패키지들로 이루어진 대규모 프로젝트의 경우에는 14장 ‘카고 작업공간’ 절에서 다룰 예정인, 카고에서 제공하는 작업공간 (workspace) 기능을 이용합니다.
- 또한 세부 구현을 캡슐화하면 더 고수준에서 코드를 재사용할 수 있는 방법에 대해서도 설명
- 일단 어떤 연산을 구현하면, (그 구현체의 작동 방식을 몰라도) 다른 코드에서 공개 인터페이스를 통해 해당 코드를 호출할 수 있음
- 코드를 작성하는 방식에 따라 아래 구현 세부 사항이 정의
- 다른 코드가 사용할 수 있는 공개 부분
- 변경 권한을 작성자에게 남겨두는 비공개 부분
- 이는 머릿속에 기억해 둬야 하는 세부 사항의 양을 제한하는 또 다른 방법
1. 패키지와 크레이트
- 크레이트 (crate) 는 러스트가 컴파일 한 차례에 고려하는 가장 작은 코드 단위
- 컴파일러는 그 파일이 크레이트라고 생각
- 크레이트는 여러 모듈을 담을 수 있고, 다음 절에서 곧 알게 되겠지만
- 모듈은 이 크레이트와 함께 컴파일되는 다른 파일들에 정의되어 있을 수도 있습니다.
- 크레이트는 바이너리일 수도 있고, 라이브러리일 수도 있습니다.
- 바이너리 크레이트 (binary crate):
- 커맨드 라인 프로그램이나 서버처럼
실행 가능한 실행파일로 컴파일할 수 있는 프로그램
- 실행파일이 실행되면 -> 무슨 일이 일어나는지를 정의한
main 함수
를 포함하고 있어야 함
- 라이브러리 크레이트 (library crate)
- main 함수를 가지고 있지 않고 실행파일 형태로 컴파일되지 않습니다.
- 그 대신, 여러 프로젝트에서 공용될 의도로 만들어진 기능들이 정의되어 있음
- 예를 들어, 2장에서 사용한 rand 크레이트는 난수를 생성하는 기능을 제공
- (엄격한 정의로썬 틀리나, 보편적으로) crate는 library crate를 말하는 경우가 많으며, library로 더 줄여서 말하기도 한다.
- 크레이트 루트 (crate root)
- 러스트 컴파일러가 컴파일을 시작하는 소스 파일
- 크레이트의 루트 모듈을 구성
- 패키지 (package)
- 크레이트들을
빌드하고, 테스트하고, 공유
하는 데 사용하는 카고 기능
- 일련의 기능을 제공하는 여러 크레이트로 구성된 번들
- 패키지에는 이 크레이트들을 빌드하는 법이 설명된
Cargo.toml 파일
이 포함되어 있음
- Cargo는 package이다.
- 실제로 코드를 빌드하는 데 사용하는 커맨드 라인 도구의 바이너리 크레이트가 포함된 패키지
- 카고 패키지에는 또한 이 바이너리 크레이트가 의존하고 있는 라이브러리 패키지도 포함되어 있음
- 패키지에는 여러 개의 바이너리 크레이트가 원하는 만큼 포함될 수 있지만, 라이브러리 크레이트는 하나만 넣을 수 있습니다.
- 프로젝트 디렉터리에는 Cargo.toml 파일이 있는데, 이것이 패키지를 만들어 줍니다.
- 카고는
패키지명과 같은 이름의 바이너리 크레이트
는 -> src/main.rs가 크레이트 루트
라는 관례를 준수
- 마찬가지로,
패키지 디렉터리에 src/lib.rs 파일이 존재할 경우
, -> 카고는 해당 패키지가 패키지명과 같은 이름의 라이브러리 크레이트를 포함하고 있다고 판단
- 그리고 그 라이브러리 크레이트의 크레이트 루트는 src/lib.rs
- 카고는 라이브러리 혹은 바이너리를 빌드할 때, 이 크레이트 루트 파일을 rustc에게 전달
- src/bin 디렉터리 내에 파일을 배치하면(main.rs 말고도) 각각의 파일이 바이너리 크레이트가 되어,
- 여러 바이너리 크레이트를 패키지에 포함할 수 있습니다.
- 현재 패키지는 src/main.rs 만 포함하고 있으므로 이 패키지는 my-project라는 이름의 바이너리 크레이트만 존재합니다.
2. 모듈을 정의하여 스코프 및 공개 여부 제어하기
- 크레이트
- 컴파일 한 차례에 고려하는 가장 작은 코드 단위
- 여러 모듈을 품고 있음
- 라이브러리나 실행 가능한 모듈로 구성된 트리 구조
- 크레이트 루트 (crate root)
- 러스트 컴파일러가 컴파일을 시작하는 소스 파일
- 크레이트의 루트 모듈을 구성
src/lib.rs
/ src/main.rs
: 맞음
src/garden.rs
아님
- 이 두 파일이 그런 이름을 갖게 된 이유는
- 모듈 트리 (module tree) 라고 불리는 크레이트 모듈 구조에서
- 최상위에 crate라는 이름을 갖는 일종의 모듈로 형성되기 때문입니다.
- module
- 구조, 스코프를 제어하고,
조직 세부 경로를 감추는 데 사용
- namespace 관리를 위한 기본 단위
- 공개/비공개 조절 가능
2.1. 모듈 치트 시트
- 크레이트 루트부터 시작:
- 크레이트를 컴파일할 때, 컴파일러는 먼저
크레이트 루트
파일을 봅니다.
- 보통은 라이브러리 크레이트의 경우 src/lib.rs
- 바이너리 크레이트의 경우 src/main.rs
- 모듈 선언:
- 크레이트 루트 파일에는 새로운 모듈을 선언할 수 있습니다;
mod garden;
이라는 코드로 ‘garden’ 모듈을 선언할 수 있음
- 컴파일러는 아래의 장소에서 이 모듈의 코드가 있는지 살펴볼 것입니다:
mod garden
뒤에 세미콜론 대신 중괄호를 써서 안쪽에 코드를 적은 인라인
src/garden.rs 파일 안
src/garden/mod.rs 파일 안
- 서브모듈 선언:
- 크레이트 루트(
src/lib.rs
/ src/main.rs
)가 아닌 다른 파일(src/garden.rs
)에서는 서브모듈 (submodule) 을 선언할 수 있습니다.
- 예를 들면 src/garden.rs 안에
mod vegetables;
를 선언할 수도 있습니다.
- 컴파일러는 부모 모듈 이름(
garden
)의 디렉터리 안쪽에 위치한 아래의 장소들에서 이 서브모듈의 코드가 있는지 살펴볼 것입니다:
mod vegetables 뒤에 세미콜론 대신 중괄호를 써서 안쪽에 코드를 적은 인라인
src/garden/vegetables.rs 파일 안
- src/garden/vegetables/mod.rs 파일 안
- 모듈 내 코드로의 경로:
- 일단 모듈이 크레이트의 일부로서 구성되면,
- 공개 규칙이 허용하는 한도 내에서라면, 해당 코드의 경로를 사용하여
- 동일한 크레이트의 어디에서든 이 모듈의 코드를 참조할 수 있게 됨
- 예를 들면, garden vegetables 모듈 안에 있는 Asparagus 타입은
crate::garden::vegetables::Asparagus
로 찾아 쓸 수 있습니다.
- 비공개 vs 공개:
- 모듈 내의 코드는 기본적으로 부모 모듈에게 비공개 (private)
- 모듈을 공개 (public) 로 만들려면, mod 대신
pub mod
를 써서 선언하세요.
- 공개 모듈의 아이템들을 공개하려면 마찬가지로 그 선언 앞에 pub을 붙이세요.
- use 키워드:
- 어떤 스코프 내에서 use 키워드는 긴 경로의 반복을 줄이기 위한 어떤 아이템으로의 단축경로를 만들어 줍니다.
crate::garden::vegetables::Asparagus
를 참조할 수 있는 모든 스코프에서 use crate::garden::vegetables::Asparagus;
로 단축경로를 만들 수 있으며,
- 그 이후부터는 스코프에서 이 타입을 사용하려면 Asparagus만 작성해주면 됩니다.
backyard
├── Cargo.lock
├── Cargo.toml
└── src
├── garden
│ └── vegetables.rs
├── garden.rs
└── main.rs
- 크레이트 루트 파일은 src/main.rs
- src/main.rs
use crate::garden::vegetables::Asparagus;
pub mod garden;
fn main() {
let plant = Asparagus {};
println!("I'm growing {:?}!", plant);
}
pub mod vegetables;
#[derive(Debug)]
pub struct Asparagus {}
2.2. 모듈로 관련된 코드 묶기
- 크레이트 루트 (crate root)
- 러스트 컴파일러가 컴파일을 시작하는 소스 파일
- 크레이트의 루트 모듈을 구성
src/lib.rs
/ src/main.rs
: 맞음
src/garden.rs
아님
- 이 두 파일이 그런 이름을 갖게 된 이유는
- 모듈 트리 (module tree) 라고 불리는 크레이트 모듈 구조에서
- 최상위에 crate라는 이름을 갖는 일종의 모듈로 형성되기 때문입니다.
- 크레이트
- 컴파일 한 차례에 고려하는 가장 작은 코드 단위
- 여러 모듈을 품고 있음
- 라이브러리나 실행 가능한 모듈로 구성된 트리 구조
- module
- 크레이트의 코드를 -> 읽기 쉽고 재사용하기도 쉽게끔 구조화를 할 수 있게 해 줌
- 구조, 스코프를 제어하고,
조직 세부 경로를 감추는 데 사용
- namespace 관리를 위한 기본 단위
- 공개/비공개 조절 가능
- 모듈 내의 코드는 기본적으로 비공개이므로, 모듈은 아이템의 공개 여부 (privacy) 를 제어하도록 해주기도 합니다.
- 비공개 아이템은 외부에서의 사용이 허용되지 않는 내부의 세부 구현입니다.
- 모듈과 모듈 내 아이템을 선택적으로 공개할 수 있는데,
- 이렇게 하여 외부의 코드가 모듈 및 아이템을 의존하고 사용할 수 있도록 노출해 줌
- 중첩 (nested) 모듈 안에 함수를 집어넣어 구성하면
크레이트 구조를 실제 레스토랑이 일하는 방식과 동일하게 구성할 수 있습니다.
- src/lib.rs
- src/front_of_house.rs 가 있어야하고
- src/front_of_house/hosting.rs 와 src/front_of_house/serving.rs 가 있어야 함.
mod front_of_house {
mod hosting {
fn add_to_waitlist() {}
fn seat_at_table() {}
}
mod serving {
fn take_order() {}
fn serve_order() {}
fn take_payment() {}
}
}
- 모듈의 본문은 중괄호로 감싸져 있습니다.
- hosting, serving 모듈처럼, 모듈 내에는 다른 모듈을 넣을 수 있습니다.
- 모듈에는 구조체, 열거형, 상수, 트레이트, 함수(예제 7-1처럼) 등의 아이템 정의 또한 가질 수 있습니다.
- 모듈을 사용함으로써 관련된 정의들을 하나로 묶고 어떤 연관성이 있는지 이름을 지어줄 수 있습니다.
- 모듈화된 코드를 사용하는 프로그래머가 자신에게 필요한 어떠한 정의를 찾을 때,
- 모든 정의를 읽어 내릴 필요 없이 그룹 기반으로 탐색할 수 있으므로 훨씬 쉽게 찾아낼 수 있죠.
- 코드에 새로운 기능을 추가하려는 프로그래머도, 자신이 어디에 코드를 작성해야 프로그램 구조가 그대로 유지되는지 파악할 수 있음
- 모듈 트리 (module tree) = 크레이트 모듈 구조
crate
└── front_of_house
├── hosting
│ ├── add_to_waitlist
│ └── seat_at_table
└── serving
├── take_order
├── serve_order
└── take_payment
- 이 트리는 또한 어떤 모듈이 서로 형제 (sibling) 관계에 있는지 나타내기도 하는데, 이는 동일한 모듈 내에 정의되어 있음을 말합니다;
- hosting과 serving은 front_of_house 모듈 내에 정의된 형제
- 모듈 A가 모듈 B 안에 있으면, 모듈 A는 모듈 B의 자식이며, 모듈 B는 모듈 A의 부모라고 말합니다.
- 디렉터리에서 파일을 찾는 것처럼, 우리는 크레이트 모듈구조에서, 모듈을 찾아낼 방법이 필요하죠.
3. 경로를 사용하여 모듈 트리의 아이템 참조하기
- 절대 경로 (absolute path)
- 크레이트 루트로부터 시작되는 전체 경로입니다;
- 외부 크레이트로부터의 코드에 대해서는
- 현재의 크레이트로부터의 코드에 대해서는 crate 리터럴로부터 시작됩니다.
- 상대 경로 (relative path)
- 현재의 모듈을 시작점으로 하여
- self, super 혹은 현재 모듈 내의 식별자를 사용
crate
└── front_of_house
├── hosting
│ ├── add_to_waitlist
│ └── seat_at_table
└── serving
├── take_order
├── serve_order
└── take_payment
- src/lib.rs (crate 모듈을 생성하는 소스파일 / front_of_house모듈이 정의)
mod front_of_house {
mod hosting {
fn add_to_waitlist() {}
}
}
pub fn eat_at_restaurant() {
crate::front_of_house::hosting::add_to_waitlist();
front_of_house::hosting::add_to_waitlist();
}
- 위 코드, 절대 경로와 상대 경로를 지정하는 방법을 잘 보여준다.
- 상대 경로, 절대 경로 중 무엇을 사용할지는 아래에 따라 여러분이 결정해야 할 사항입니다.
- 프로젝트에 따라,
- 아래 2가지 중 선택 (
아이템을 정의하는 코드
와 아이템을 사용하는 코드
를)
- 분리하고 싶은지, -> 절대 경로
- 혹은 같이 두고 싶은지에 -> 상대 경로
- 일반적으로 선호하는 경로는 절대 경로입니다.
- 아이템을 정의하는 코드와 호출하는 코드는 분리되어 있을 가능성이 높기 때문입니다.
- 하지만,
hosting
모듈이 pub 선언되어 있지 않아, 빌드되지 않는다.
- hosting 모듈과 add_to_waitlist 함수의 경로를 정확히 명시했지만, 해당 영역은 비공개 영역이기 때문에 러스트가 접근을 허용하지 않습니다.
- 러스트에서는 모든 아이템이 기본적으로 부모 모듈에 대해 비공개입니다.
- 함수나 구조체 같은 아이템을 비공개로 하고 싶다면 모듈에 넣으면 됩니다.
- 부모 모듈 내 아이템은 자식 모듈 내 비공개 아이템을 사용할 수 없지만,
- 자식 모듈 내 아이템은 부모 모듈 내 아이템을 사용할 수 있습니다.
- 레스토랑 고객들은 내부에서 진행되는 일을 알 수 없지만, 사무실 관리자는 자신이 운영하는 레스토랑의 모든 것을 보고, 행동할 수 있습니다.
- 러스트에서는 pub 키워드를 사용하여 자식 모듈의 내부 구성 요소를 공개 (public) 함으로써 -> 외부의 상위 모듈로 노출할 방법을 제공
3.1. 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();
}
- front_of_house 모듈은 공개가 아니지만, eat_at_restaurant 함수와 front_of_house 모듈은 같은 모듈 내에 정의되어 있으므로 (즉, 서로 형제 관계이므로) eat_at_restaurant 함수에서 front_of_house 모듈을 참조할 수 있습니다.
3.2. 바이너리와 라이브러리가 함꼐 있는 패키지를 위한 최고의 예제
- 통상적으로 이렇게 라이브러리와 바이너리 크레이트 모두를 가지는 패턴의 패키지들은,
- 라이브러리 크레이트에 있는 코드를 호출하여 실행파일을 시작하기 위한 양만큼의 코드가 바이너리 크레이트에 담긴 형태가 됩니다.
- 모듈 트리는 src/lib.rs 내에 정의되어야 합니다.
- 그러면 바이너리 크레이트 내에서는 패키지 이름으로 시작하는 경로를 사용함으로써 모든 공개 아이템을 사용할 수 있습니다.
- 바이너리 크레이트는 (완전히 외부에 있는 다른 크레이트가 이 라이브러리 크레이트를 사용하는 식과 동일하게) 이 라이브러리 크레이트의 사용자가 됩니다: 즉 공개 API만 사용할 수 있습니다.
3.3. super로 시작하는 상대 경로
- super로 시작하면, (현재 모듈 혹은 크레이트 루트 대신) 자기 부모 모듈부터 시작되는 상대 경로를 만들 수 있습니다.
- 이는 파일시스템 경로에서 ..로 시작하는 것과 동일합니다.
- 예제 7-8은 셰프가 잘못된 주문을 수정하여 고객에게 직접 전달하는 상황을 묘사한 코드
- back_of_house 모듈에 정의된 fix_incorrect_order 함수는
- super로 시작하는 deliver_order로의 경로를 특정하는 것으로 부모 모듈에 정의된 deliver_order 함수를 호출합니다:
- src/lib.rs
fn deliver_order() {}
mod back_of_house {
fn fix_incorrect_order() {
cook_order();
super::deliver_order();
}
fn cook_order() {}
}
- back_of_house 모듈과 deliver_order 함수는 크레이트 모듈 구조 변경 시 서로의 관계를 유지한 채 함께 이동될 가능성이 높습니다.
- 그러므로 super를 사용하면, 차후에 다른 모듈에 이동시키더라도 수정해야 할 코드를 줄일 수 있습니다.
3.4. 구조체/열거형을 공개하기
- 구조체(struct) 정의에 pub를 쓰면 구조체는 공개되지만, 구조체의 필드는 비공개로 유지됩니다.
- 공개 여부는 각 필드마다 정할 수 있습니다.
- src/lib.rs
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"),
}
}
}
}
pub fn eat_at_restaurant() {
let mut meal = back_of_house::Breakfast::summer("Rye");
meal.toast = String::from("Wheat");
println!("I'd like {} toast please", meal.toast);
}
- 또한, back_of_house::Breakfast 구조체는 비공개 필드를 갖고 있기 때문에, Breakfast 인스턴스를 생성할 공개 연관 함수(예제에서는 summer 함수입니다)를 반드시 제공해야 합니다.
- 만약 Breakfast 구조체에 그런 함수가 존재하지 않을 경우, eat_at_restaurant 함수에서 Breakfast 인스턴스를 생성할 수 없습니다.
- 반대로, 열거형은 공개로 지정할 경우 모든 배리언트가 공개됩니다.
- 열거형은 그 배리언트가 공개되지 않는다면 큰 쓸모가 없습니다;
- 열거형의 모든 배리언트에 대해 전부 pub을 붙이는 것은 귀찮은 일이 될 것이므로, 열거형의 배리언트는 기본적으로 공개
- 구조체의 경우 필드를 공개로 하지 않는 것이 종종 유용하므로, 구조체 필드는 pub을 명시하지 않는 한 기본적으로 모든 것이 비공개라는 일반적인 규칙을 따릅니다.
4. use 키워드로 경로를 스코프 안으로 가져오기
- 함수 호출을 위해서 경로를 작성하는 것은 불편하고 반복적인 느낌을 줄 수 있음
- use 키워드를 한번 사용하여 어떤 경로의 단축경로 (shortcut) 를 만들 수 있고, 그러면 스코프 안쪽 어디서라도 짧은 이름을 사용할 수 있음
- src/lib.rs
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
- use가 사용된 특정한 스코프에서만 단축경로가 만들어진다는 점을 주의하세요.
- 아래 예제에서는 eat_at_restaurant 함수를 새로운 자식 모듈 customer로 옮겼는데, 이러면 use 구문과 다른 스코프가 되므로, 이 함수는 컴파일 되지 않습니다:
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
use crate::front_of_house::hosting;
mod customer {
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
}
4.1. 보편적인 use 경로 작성법
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
use crate::front_of_house::hosting;
mod customer {
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
}
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
use crate::front_of_house::hosting::add_to_waitlist;
pub fn eat_at_restaurant() {
add_to_waitlist();
}
- 함수 가져올 때: 부모 모듈을 특정해라!
- 예제 1과 2의 동작은 동일하지만, 예제 1 코드가 use 키워드로 스코프에 함수를 가져올 때의 관용적인 코드
- 함수의 부모 모듈을 use 키워드로 가져오면 함수를 호출할 때 부모 모듈을 특정해야 합니다.
- 함수 호출 시 부모 모듈을 특정하면 전체 경로를 반복하는 것을 최소화하면서도 함수가 로컬에 정의되어 있지 않음을 명백히 보여주게 됩니다.
- 예제2의 코드는 add_to_waitlist가 어디에 정의되어 있는지 불분명합니다.
- 구조체/열거형 가져올 떄: 아이템만 특정하는것이 더 보편적?
- 한편, use 키워드로 구조체나 열거형 등의 타 아이템을 가져올 시에는 전체 경로를 작성하는 것이 보편적입니다.
- 아래 코드는 HashMap 표준 라이브러리 구조체를 바이너리 크레이트의 스코프로 가져오는 관용적인 코드 예시입니다.
- src/main.rs
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert(1, 2);
}
- 이러한 관용이 탄생하게 된 명확한 이유는 없습니다. 어쩌다 보니 관습이 생겼고, 사람들이 이 방식대로 러스트 코드를 읽고 쓰는 데에 익숙해졌을 뿐입니다.
- 하지만, 동일한 이름의 아이템을 여럿 가져오는 경우는 이 방식을 사용하지 않습니다. 러스트가 허용하지 않기 때문이죠.
- 아래 예시도 보편적 예시
- src/lib.rs
use std::fmt;
use std::io;
fn function1() -> fmt::Result {
}
fn function2() -> io::Result<()> {
}
4.2. as 키워드로 새로운 이름 제공하기
- use 키워드로 동일한 이름의 타입을 스코프로 여러 개 가져올 경우의 또 다른 해결 방법이 있습니다.
-또 다른 보편적 예시 2
use std::fmt::Result;
use std::io::Result as IoResult;
fn function1() -> Result {
}
fn function2() -> IoResult<()> {
}
4.3. pub use로 다시 내보내기
- use 키워드로 이름을 가져올 경우, 해당 이름은 새 위치의 스코프에서 비공개가 됩니다.
- pub과 use를 결합하면 우리 코드를 호출하는 코드가 해당 스코프에 정의된 것처럼 해당 이름을 참조할 수 있습니다.
- 이 기법은 아이템을 스코프로 가져오는 동시에 다른 곳에서 아이템을 가져갈 수 있도록 만들기 때문에, 다시 내보내기 (re-exporting) 라고 합니다.
- src/lib.rs (다른 스코프의 코드에서 사용할 수 있도록 pub use 사용)
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();
}
- 중요!!!
- 위와 같이 변경하기 전이라면
- 외부 코드에서는 add_to_waitlist 함수를 호출하기 위해
restaurant::front_of_house::hosting::add_to_waitlist()
라는 경로를 사용해야 할 것입니다.
- 위의 pub use가 루트 모듈로부터 hosting 모듈을 다시 내보냈으므로, 이제 외부 코드는
restaurant::hosting::add_to_waitlist()
경로를 대신 사용할 수 있습니다.
- 중요!!!
- 다시 내보내기 기법은 작성한 코드의 구조 내부와, 그 코드를 사용할 프로그래머들이 예상할법한 해당 분야의 구조가 서로 다를 때 유용
- 레스토랑 비유 예제를 예로 들어보죠. 레스토랑을 운영하는 직원들의 머릿속에서는 ‘접객 부서’와 ‘지원 부서’가 나뉘어 있습니다.
- 하지만 레스토랑을 방문하는 고객들은 레스토랑의 부서를 그런 용어로 나누어 생각하지 않겠죠.
- pub use를 사용하면 코드를 작성할 때의 구조와, 노출할 때의 구조를 다르게 만들 수 있습니다.
- 라이브러리를 제작하는 프로그래머와, 라이브러리를 사용하는 프로그래머 모두를 위한 라이브러리를 구성하는 데 큰 도움이 되죠.
- TODO: pub use에 대한 또 다른 예제, 그리고 이것이 여러분의 크레이트 문서에 어떤 영향을 주는지에 대해서는 14장의 ‘pub use를 사용하여 편리한 공개 API 내보내기’절에서 살펴보겠습니다
4.4. 외부 패키지 사용하기
- rand 패키지를 프로젝트에서 사용하기 위해서 Cargo.toml에 다음 줄을 추가했었죠:
rand = "0.8.5"
- Cargo.toml에 rand를 의존성으로 추가하면
- 카고가 crates.io에서 rand 패키지를 비롯한 모든 의존성을 다운로드하고
- 프로젝트에서 rand 패키지를 사용할 수 있게 됩니다.
use rand::Rng;
fn main() {
let secret_number = rand::thread_rng().gen_range(1..=100);
}
- 알아 두어야 할 것이 있다면 std 표준 라이브러리도 마찬가지로 외부 크레이트
- 러스트 언어에 포함되어 있기 때문에 Cargo.toml 에 추가할 필요는 없지만,
- 표준 라이브러리에서 우리가 만든 패키지의 스코프로 가져오려면 use 문을 작성해야 합니다.
use std::collections::HashMap;
4.5. 중첩 경로를 사용하여 대량의 use 나열을 정리하기
use std::cmp::Ordering;
use std::io;
use std::io;
use std::io::Write;
use std::{cmp::Ordering, io};
use std::io::{self, Write};
4.6. glob 연산자
- 경로에 글롭 (glob) 연산자 *를 붙이면 경로 안에 정의된 모든 공개 아이템을 가져올 수 있습니다.
use std::collections::*;
- 하지만 글롭 연산자는 아래 용도 말고는 쓰지 말자.
- 글롭 연산자는 테스트할 모든 아이템을 tests 모듈로 가져오는 용도로 자주 사용됩니다. (11장 ‘테스트 작성 방법’에서 다룰 예정입니다.)
- TODO: 또한 프렐루드 패턴의 일부로 사용되기도 하며, 자세한 내용은 표준 라이브러리 문서를 참고 바랍니다.
- 프렐루드: 자주 사용하는 라이브러리의 아이템들을 포함하는
모듈
5. 별개의 파일로 모듈 분리하기
- 큰 모듈이라면, 정의를 여러 파일로 나누어 코드를 쉽게 찾아갈 수 있도록 만들어야겠죠.
- 크레이트 루트 파일에 모든 모듈이 정의되는 형태 대신 이 모듈들을 파일로 추출해 보겠습니다.
- 이 경우 크레이트 루트 파일은 src/lib.rs지만, 이러한 절차는 크레이트 루트 파일이 src/main.rs인 바이너리 크레이트에서도 작동합니다.
- src/lib.rs
mod front_of_house;
pub use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
pub mod hosting {
pub fn add_to_waitlist() {}
}
- 모듈 트리에서 mod 선언을 사용하여 파일을 로드하는 것은 한 번만 하면 됩니다. 일단 그 파일이 프로젝트의 일부란 것을 컴파일러가 파악하면 프로젝트의 다른 파일들은 선언된 위치의 경로를 사용하여 로드된 파일의 코드를 참조해야 합니다.
- 다음으로 hosting 모듈을 파일로 추출하겠습니다. hosting이 루트 모듈이 아니라 front_of_house의 자식 모듈이기 때문에 과정이 약간 다릅니다.
- src/front_of_house/ 에 위치시키겠습니다.
- src/fromt_of_house.rs
pub mod hosting;
- src/fromt_of_house/hosting.rs
pub fn add_to_waitlist() {}
5.1. 대체파일경로
- src/front_of_house.rs (우리가 지금 다룬 형태)
- src/front_of_house/mod.rs (예전 스타일, 여전히 지원되는 경로)
- mod 키워드는 모듈을 선언하고, 러스트는 모듈과 같은 이름의 파일에서 해당 모듈에 들어가는 코드를 찾습니다.