모듈 경로와 아이템 참조
절대·상대 경로와 super로 모듈 아이템을 찾고 pub를 구조체와 열거형에 적용해 외부 접근 범위를 제어합니다.
모듈 트리에서 아이템을 찾을 때는 파일 시스템에서 파일을 찾을 때처럼 경로(path)를 씁니다. 함수를 부르려면 그 함수의 경로를 알아야 합니다. 경로에는 두 가지 형태가 있습니다. 절대 경로(absolute path)는 크레이트 루트에서 시작하는 전체 경로로, 바깥 크레이트의 코드라면 그 크레이트의 이름으로, 지금 크레이트의 코드라면 crate라는 말로 시작합니다. 상대 경로(relative path)는 지금의 모듈에서 시작하며, self나 super, 또는 지금 모듈 안의 이름으로 시작합니다. 두 경로 모두 뒤에 ::로 나눈 이름이 하나 이상 이어집니다.
이 글에서는 두 경로로 아이템을 부르는 방법과, pub으로 아이템을 공개하는 방법, 부모 모듈을 가리키는 super, 그리고 구조체와 열거형을 공개할 때의 규칙을 살펴봅니다. 아래 표는 경로마다 어디서 시작하는지, 그리고 이름을 찾는 일과 그 이름을 쓸 수 있는지 확인하는 일이 따로라는 점을 정리한 것입니다.
PATH RESOLUTION · VISIBILITY
경로는 이름을 찾고, 가시성은 그 이름을 쓸 수 있는지 결정한다
먼저 경로의 기준점에서 각 이름을 해석한 뒤, 호출 위치에서 대상까지 접근 가능한지 따로 검사합니다. 모듈 트리는 논리적인 이름 계층이며 파일·디렉터리 배치와 같은 구조라는 뜻은 아닙니다.
1 · 이름 해석
crate::, self::, super:: 또는 현재 스코프의 이름에서 출발해 ::로 이어진 각 세그먼트를 찾습니다.
2 · 접근 판정
경로가 존재해도 현재 모듈의 private 접근 규칙이나 공개 조상 경계를 통과하지 못하면 사용할 수 없습니다. pub은 이름 해석을 대신하지 않습니다.
| 경로 형식 | 이름을 찾기 시작하는 곳 | 이동 내성과 선택 기준 |
|---|---|---|
crate::… |
현재 크레이트의 루트 | 호출 코드만 이동해도 대상의 루트 기준 경로가 같으면 유지됩니다. 대상의 루트 기준 위치가 바뀌면 수정합니다. |
name::… |
현재 이름 해석 스코프의 name |
호출자와 대상의 상대 관계가 유지될 때 유리합니다. 현재 모듈 아이템, 가져온 이름, 외부 크레이트 이름 등이 첫 세그먼트가 될 수 있습니다. |
self::… |
현재 모듈 | 현재 모듈 기준임을 명시합니다. 호출자와 대상 모듈을 함께 옮겨 관계가 유지되면 경로도 유지됩니다. |
super::… |
현재 모듈의 부모 | 필요하면 super::super::…처럼 반복합니다. 부모·자식 관계를 함께 옮길 때 수정 범위를 줄일 수 있습니다. |
| 외부 크레이트 경로 | 호출자에게 바인딩된 외부 크레이트 이름 | Cargo 의존성 이름이나 별칭을 바꾸면 첫 세그먼트도 바뀝니다. 공개 재공개 경로가 유지되면 내부 모듈 이동을 숨길 수 있습니다. |
crate::…- 현재 크레이트 루트에서 시작합니다.
- 호출자만 이동할 때 안정적이며, 대상의 루트 기준 위치가 바뀌면 수정합니다.
name::…- 현재 스코프의 모듈 아이템, 가져온 이름, 외부 크레이트 이름 등에서 첫 이름을 해석합니다.
- 호출자와 대상의 상대 관계가 유지될 때 유리합니다.
self::…- 현재 모듈에서 시작함을 명시합니다.
- 관련 모듈을 함께 옮겨 관계가 유지되면 경로도 유지됩니다.
super::…- 부모 모듈에서 시작하며 반복할 수 있습니다.
- 부모·자식 관계를 함께 이동할 때 수정 범위를 줄일 수 있습니다.
- 외부 크레이트 경로
- 호출자에게 바인딩된 외부 크레이트 이름에서 시작합니다.
- Cargo 의존성 이름이나 별칭을 바꾸면 첫 세그먼트도 바뀝니다. 공개 재공개 경로가 유지되면 내부 모듈 이동을 숨길 수 있습니다.
절대 경로와 상대 경로 중 언어 차원의 우선 선택은 없습니다. 유지하려는 이동 경계를 고른 뒤, 별도로 호출 위치에서 각 경로 세그먼트와 최종 아이템의 가시성을 확인합니다.
절대 경로와 상대 경로
모듈 스코프와 공개 범위의 식당 예제로 돌아가, add_to_waitlist 함수를 부르고 싶다고 합시다. 곧 add_to_waitlist의 경로는 무엇이냐는 질문입니다. 다음 코드는 앞의 예제에서 모듈과 함수를 일부 덜어 내고, 크레이트 루트에 정의한 새 함수 eat_at_restaurant에서 add_to_waitlist를 두 가지 방법으로 부릅니다. 두 경로는 모두 맞지만, 다른 문제가 있어 이 코드는 아직 컴파일되지 않습니다. eat_at_restaurant는 이 라이브러리 크레이트의 공개 API이므로 pub을 붙였으며, pub은 뒤에서 자세히 다룹니다.
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();
}첫 호출은 절대 경로를 씁니다. add_to_waitlist는 eat_at_restaurant와 같은 크레이트에 정의되어 있으므로 절대 경로를 crate로 시작하고, 그 뒤에 add_to_waitlist까지 이어지는 모듈을 차례로 적습니다. 파일 시스템에 비유하면 /front_of_house/hosting/add_to_waitlist처럼 루트 /에서 시작하는 경로를 적는 것과 같습니다. 둘째 호출은 상대 경로를 씁니다. 모듈 트리에서 eat_at_restaurant와 같은 곳에 정의된 front_of_house로 시작하므로, 파일 시스템으로 치면 front_of_house/hosting/add_to_waitlist입니다. 모듈 이름으로 시작하는 경로가 상대 경로입니다.
어느 쪽을 쓸지는 프로젝트에 따라, 그리고 아이템을 정의하는 코드와 쓰는 코드를 따로 옮길 가능성이 큰지 함께 옮길 가능성이 큰지에 따라 정합니다. 예를 들어 front_of_house와 eat_at_restaurant를 함께 customer_experience라는 모듈 안으로 옮기면, 절대 경로는 고쳐야 하지만 상대 경로는 그대로 맞습니다. 반대로 eat_at_restaurant만 dining이라는 모듈로 옮기면, 절대 경로는 그대로이지만 상대 경로는 고쳐야 합니다. 일반적으로는 정의와 사용을 따로 옮길 가능성이 더 크므로 절대 경로를 더 많이 씁니다.
그러면 이 코드가 컴파일되지 않는 까닭을 봅니다.
error[E0603]: module `hosting` is private
--> src/lib.rs:9:28
|
9 | crate::front_of_house::hosting::add_to_waitlist();
| ^^^^^^^ private module
|
note: the module `hosting` is defined here
--> src/lib.rs:2:5
|
2 | mod hosting {
| ^^^^^^^^^^^같은 E0603 오류가 12번째 줄의 상대 경로에도 하나 더 나옵니다. hosting 모듈이 비공개(private)라는 오류입니다. hosting과 add_to_waitlist까지의 경로는 맞게 적었지만, 그 부분이 비공개이므로 러스트가 접근을 허용하지 않습니다. 러스트에서 함수, 메서드, 구조체, 열거형, 모듈, 상수 같은 아이템은 기본으로 부모 모듈에게 비공개입니다. 그래서 함수나 구조체를 감추고 싶다면 모듈에 넣으면 됩니다.
부모 모듈의 아이템은 자식 모듈 안의 비공개 아이템을 쓸 수 없지만, 자식 모듈의 아이템은 조상 모듈의 아이템을 쓸 수 있습니다. 자식 모듈은 구현의 세부를 감싸 감추지만, 자식 모듈은 자신이 정의된 문맥을 볼 수 있기 때문입니다. 식당에 비유하면, 손님은 주방에서 무슨 일이 일어나는지 모르지만 식당의 관리자는 자기 식당의 모든 것을 보고 할 수 있는 것과 같습니다. 러스트는 내부 구현을 기본으로 감추므로, 바깥의 코드를 깨뜨리지 않고 고칠 수 있는 코드가 어디인지 알 수 있습니다. 대신 pub 키워드로 자식 모듈의 안쪽을 바깥의 조상 모듈에 공개할 수 있습니다.
pub 키워드로 경로 노출하기
hosting이 비공개라는 오류로 돌아갑니다. 부모 모듈의 eat_at_restaurant가 자식 모듈의 add_to_waitlist를 써야 하므로, hosting 모듈에 pub을 붙입니다.
mod front_of_house {
pub 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();
}그래도 아직 컴파일되지 않습니다. 오류 번호는 앞과 같은 E0603이지만, 비공개라고 지적하는 대상이 모듈 hosting에서 함수 add_to_waitlist로 바뀌었습니다.
error[E0603]: function `add_to_waitlist` is private
--> src/lib.rs:9:37
|
9 | crate::front_of_house::hosting::add_to_waitlist();
| ^^^^^^^^^^^^^^^ private function
|
note: the function `add_to_waitlist` is defined here
--> src/lib.rs:3:9
|
3 | fn add_to_waitlist() {}
| ^^^^^^^^^^^^^^^^^^^^mod hosting 앞에 pub을 붙여 모듈은 공개되었으므로, front_of_house에 접근할 수 있는 곳이라면 hosting에도 접근할 수 있습니다. 하지만 hosting의 내용은 여전히 비공개입니다. 모듈의 pub은 조상 모듈이 그 모듈을 가리킬 수 있게 할 뿐 그 안의 코드에 접근하게 하지는 않습니다. 모듈은 담는 그릇일 뿐이라 모듈만 공개해서는 할 수 있는 일이 별로 없으며, 그 안의 아이템도 공개해야 합니다. 오류는 이번에는 add_to_waitlist가 비공개라고 알려 줍니다. 비공개 규칙은 구조체, 열거형, 함수, 메서드, 모듈에 모두 적용되므로, 함수에도 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();
}이제 컴파일됩니다. 절대 경로는 모듈 트리의 맨 위 crate에서 시작하며, front_of_house는 크레이트 루트에 정의되어 있습니다. front_of_house는 공개되지 않았지만, eat_at_restaurant와 같은 모듈에 정의된 형제이므로 eat_at_restaurant는 front_of_house를 쓸 수 있습니다. 그다음 hosting은 pub이고 그 부모에 접근할 수 있으니 hosting에도 접근할 수 있으며, add_to_waitlist도 pub이고 부모에 접근할 수 있으니 부를 수 있습니다. 상대 경로는 크레이트 루트 대신 front_of_house에서 시작한다는 점만 다르고, 나머지 판단은 같습니다.
아래 표는 부르는 곳이 같은 크레이트 안인지 바깥 크레이트인지에 따라, 그리고 pub을 무엇에 붙였는지에 따라 무엇을 쓸 수 있는지 정리한 것입니다.
CALL SITE · PUBLIC API
pub은 붙인 아이템의 가시성만 넓힌다
같은 패키지 안에서도 호출자가 같은 크레이트인지 외부 크레이트인지에 따라 통과해야 할 경계가 다릅니다. 모듈을 공개해도 내용은 자동 공개되지 않으며, 외부의 원래 경로는 공개된 조상과 최종 아이템이 모두 이어져야 합니다.
같은 크레이트 · 루트의 호출자
eat_at_restaurant와 private front_of_house는 루트에 함께 선언된 형제이므로 루트 호출자가 모듈 이름을 볼 수 있습니다. 그 안으로 들어가려면 hosting과 add_to_waitlist가 각각 호출 위치에 보이도록 공개되어야 합니다.
외부 크레이트 · 라이브러리 사용자
라이브러리 타깃의 크레이트 이름에서 시작해 공개 경로만 사용합니다. crate::는 호출자 자신의 크레이트를 뜻합니다. 공개 재공개 경로는 private 내부 구조를 숨길 수 있습니다.
| 선언 | 허용되는 것 | 남는 경계 |
|---|---|---|
| 공개 모듈 | 모듈 선언에 pub을 붙이면 접근 가능한 부모에서 그 모듈 이름을 지나갈 수 있습니다. |
모듈 안의 함수·타입·필드는 각자의 가시성을 그대로 유지합니다. |
| 공개 함수 | 함수 선언에 pub을 붙이고 호출 위치에서 함수까지 이어지는 경로가 접근 가능하면 호출할 수 있습니다. |
함수만 공개해도 private 조상 모듈의 원래 경로를 통과하지는 못합니다. |
pub(crate)크레이트 한정 공개 |
선언한 아이템의 최대 범위를 현재 크레이트 전체로 제한합니다. | 경로의 다른 private 조상을 자동으로 열지 않으며 외부 크레이트에는 공개되지 않습니다. |
| 공개 구조체 | 구조체 선언의 pub은 타입 이름을 공개하고, pub인 필드만 외부에서 직접 읽거나 쓸 수 있게 합니다. |
접근할 수 없는 필드가 하나라도 있으면 외부 구조체 표현식으로 직접 생성할 수 없어 공개 생성 API가 필요합니다. |
| 공개 열거형 | 열거형 선언에 pub을 붙여 접근할 수 있게 하면 그 배리언트도 공개됩니다. |
배리언트마다 pub을 붙이지 않습니다. |
| 라이브러리 ↔ 바이너리 | Cargo 기본 루트는 각각 src/lib.rs와 src/main.rs이며 서로 다른 크레이트입니다. |
[lib]·[[bin]]으로 이름과 경로를 바꿀 수 있습니다. 바이너리는 라이브러리 크레이트 이름과 공개 API를 사용하며 라이브러리의 크레이트 한정 아이템에는 접근할 수 없습니다. |
- 공개 모듈
- 모듈 선언의
pub은 모듈 이름의 가시성만 넓힙니다. - 내부 함수·타입·필드는 각각 별도로 공개해야 합니다.
- 공개 함수
- 함수 선언의
pub은 접근 가능한 경로를 통해 함수를 호출할 수 있게 합니다. - private 조상의 원래 경로를 함수의
pub만으로 통과할 수는 없습니다. pub(crate)· 크레이트 한정 공개- 아이템의 최대 공개 범위를 현재 크레이트로 제한합니다.
- 다른 private 조상을 열지 않으며 외부 크레이트에는 보이지 않습니다.
- 공개 구조체
- 구조체 선언과 각 필드의
pub을 따로 정합니다. - private 필드가 있으면 외부 구조체 표현식은 막히므로 공개 생성 API가 필요합니다.
- 공개 열거형
- 열거형 선언에
pub을 붙이면 접근 가능한 배리언트도 공개됩니다. - 배리언트마다
pub을 붙이지 않습니다. - 라이브러리 ↔ 바이너리
- 같은 패키지 안에서도 서로 다른 크레이트입니다.
- 바이너리는 라이브러리 크레이트 이름과 공개 API를 사용합니다. 라이브러리의
pub(crate)아이템에는 접근할 수 없습니다. 타깃 이름과 루트 경로는 설정으로 바꿀 수 있습니다.
접근 오류는 호출자가 속한 크레이트 → 경로 기준점과 세그먼트 해석 → 조상 모듈과 최종 아이템의 가시성 → 사용할 수 있는 공개 재공개 경로 순서로 확인합니다.
같은 크레이트 안에서는 형제를 볼 수 있으므로 루트의 eat_at_restaurant가 비공개 front_of_house를 쓸 수 있지만, 그 안으로 들어가려면 hosting과 add_to_waitlist가 공개되어야 합니다. 바깥 크레이트에서 원래 경로로 쓰려면 front_of_house까지 모든 조상이 공개되어야 합니다. 공개 범위를 지금 크레이트 안으로만 넓히고 싶다면 pub(crate)를 쓰며, 이 표시도 경로에 있는 다른 비공개 조상을 열어 주지는 않습니다. 같은 패키지의 바이너리 크레이트도 라이브러리 크레이트에게는 바깥 크레이트이므로, pub(crate) 아이템을 쓸 수 없습니다.
라이브러리 크레이트를 다른 프로젝트와 함께 쓸 생각이라면, 공개 API가 그 크레이트를 쓰는 사람과 맺는 약속이 되므로 공개 API를 바꿀 때 고려할 것이 많습니다. 이 주제는 이 교재의 범위를 넘으며, 관심이 있다면 러스트 API 가이드라인을 참고하세요.
패키지와 크레이트에서 본 것처럼, 한 패키지에 src/main.rs의 바이너리 크레이트와 src/lib.rs의 라이브러리 크레이트를 함께 둘 수 있습니다. 보통은 바이너리 크레이트에 라이브러리의 공개 기능을 불러 실행 파일을 시작하는 데 필요한 만큼의 코드만 두고, 대부분의 기능은 라이브러리에 둡니다. 그러면 라이브러리의 기능을 다른 프로젝트도 쓸 수 있습니다. 바이너리 크레이트는 바깥의 크레이트처럼 라이브러리의 공개 API만 쓸 수 있으므로, 라이브러리를 만드는 사람이 그 사용자도 되어 좋은 API를 설계하는 데 도움이 됩니다. 11장에서 만드는 명령줄 프로그램은 모듈성과 에러 처리 향상을 위한 리팩터링에서 이런 구조로 나뉩니다.
super로 시작하는 상대 경로
super로 시작하면 지금 모듈이나 크레이트 루트가 아니라 부모 모듈에서 시작하는 상대 경로를 만들 수 있습니다. 파일 시스템 경로에서 ..로 부모 폴더를 가리키는 것과 비슷합니다. super는 부모 모듈에 있다고 알고 있는 아이템을 가리킬 때 쓰며, 모듈이 부모와 가깝게 관련되어 있어 나중에 함께 다른 곳으로 옮길 가능성이 클 때 쓸모가 있습니다. 다음 코드는 요리사가 잘못된 주문을 고쳐 직접 손님에게 가져다주는 상황을 나타냅니다.
fn deliver_order() {}
mod back_of_house {
fn fix_incorrect_order() {
cook_order();
super::deliver_order();
}
fn cook_order() {}
}fix_incorrect_order는 back_of_house 모듈 안에 있으므로, super는 back_of_house의 부모인 크레이트 루트를 가리킵니다. 거기에 deliver_order가 있으므로 이 호출은 성공합니다. 같은 모듈에 있는 cook_order는 경로 없이 바로 부릅니다. back_of_house와 deliver_order는 서로의 관계를 유지한 채 함께 옮겨질 가능성이 크므로, super를 쓰면 나중에 다른 모듈로 옮겨도 고칠 곳이 줄어듭니다. 앞의 경로 표의 그림이 바로 이 호출 관계를 보여 줍니다.
구조체, 열거형을 공개하기
구조체와 열거형에도 pub을 붙일 수 있지만 알아 둘 것이 있습니다. 구조체 정의에 pub을 붙이면 구조체는 공개되지만 필드는 여전히 비공개이며, 필드마다 공개 여부를 따로 정할 수 있습니다. 다음 코드는 공개 구조체 back_of_house::Breakfast를 정의하고, toast 필드는 공개하지만 seasonal_fruit 필드는 비공개로 둡니다. 손님은 아침 식사에 곁들일 빵을 고를 수 있지만, 과일은 요리사가 계절과 재고에 따라 정하므로 손님이 고르거나 미리 알 수 없는 상황을 나타냅니다.
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);
// 다음 줄의 주석을 풀면 컴파일되지 않음.
// 계절 과일은 보거나 바꿀 수 없음
// meal.seasonal_fruit = String::from("blueberries");
}toast는 공개 필드이므로 eat_at_restaurant에서 점 표기법으로 읽고 쓸 수 있지만, seasonal_fruit는 비공개이므로 쓸 수 없습니다. 마지막 줄의 주석을 풀면 그 필드가 비공개라는 오류가 납니다. 또 Breakfast에는 비공개 필드가 있으므로, eat_at_restaurant가 구조체 문법으로 직접 인스턴스를 만들 수 없습니다. 비공개 필드 seasonal_fruit의 값을 정할 수 없기 때문이며, 그래서 인스턴스를 만들어 주는 공개 연관 함수 summer가 필요합니다.
이 라이브러리에 다음 src/main.rs를 더해 실행하면, 라이브러리 안에서 summer("Rye")로 만든 아침 식사의 공개 필드 toast를 Wheat로 바꾼 뒤 읽어 출력합니다.
fn main() {
restaurant::eat_at_restaurant();
}I'd like Wheat toast please반면 열거형은 공개하면 배리언트가 모두 공개됩니다. enum 앞에 pub만 붙이면 됩니다.
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;
}Appetizer를 공개했으므로 eat_at_restaurant에서 Soup과 Salad를 쓸 수 있습니다. 열거형은 배리언트가 공개되지 않으면 쓸모가 거의 없고, 배리언트마다 pub을 붙이는 것은 번거로우므로 배리언트는 기본으로 공개됩니다. 구조체는 필드를 공개하지 않는 편이 유용할 때가 많으므로, pub을 붙이지 않는 한 모든 것이 비공개라는 일반 규칙을 따릅니다.
pub과 관련해 하나 더 남은 것이 모듈 시스템의 마지막 기능인 use 키워드입니다. 비공개 규칙과 pub(crate)의 자세한 내용은 Reference의 가시성 규칙에 있습니다.
정리
- 절대 경로는
crate나 바깥 크레이트의 이름으로 시작하고, 상대 경로는 지금 모듈의 이름이나self,super로 시작합니다. - 아이템은 기본으로 부모 모듈에게 비공개이며, 자식은 조상의 아이템을 쓸 수 있지만 부모는 자식의 비공개 아이템을 쓸 수 없습니다.
- 모듈에
pub을 붙여도 그 안의 아이템은 공개되지 않으므로, 쓸 아이템에도pub을 붙여야 합니다. pub(crate)는 지금 크레이트 안으로만 공개하며, 같은 패키지의 바이너리 크레이트도 쓸 수 없습니다.super는 부모 모듈에서 시작하는 상대 경로를 만듭니다.- 공개 구조체의 필드는 따로
pub을 붙여야 공개되며, 비공개 필드가 있으면 바깥에서 직접 인스턴스를 만들 수 없습니다. - 공개 열거형의 배리언트는 모두 공개됩니다.
연습 문제
1. 부르는 곳에 따라
src/lib.rs를 pub mod hosting과 pub fn add_to_waitlist가 있는 최종 버전으로 두고, front_of_house는 비공개로 둡니다. 다음 호출은 각각 컴파일되나요? (가) 라이브러리 루트의 eat_at_restaurant 안에서 crate::front_of_house::hosting::add_to_waitlist() (나) 바이너리의 main에서 restaurant::front_of_house::hosting::add_to_waitlist() (다) 바이너리의 main에서 restaurant::eat_at_restaurant() (라) (다)를 그대로 두고 라이브러리의 함수만 pub(crate) fn eat_at_restaurant()로 바꾼 경우
해설 보기
(가)는 컴파일됩니다. 크레이트 루트는 자기가 정의한 front_of_house를 쓸 수 있고, 그 아래 두 이름이 공개되어 있습니다. (나)는 컴파일되지 않습니다. 바이너리는 바깥 크레이트이므로 비공개 조상 front_of_house를 지날 수 없어 E0603 오류가 납니다. (다)는 공개된 루트 함수를 부르므로 컴파일됩니다. (라)는 pub(crate)가 라이브러리 크레이트 안으로만 공개하므로, 같은 패키지에 있더라도 바깥 크레이트인 바이너리에서는 쓸 수 없어 컴파일되지 않습니다.
2. 함께 옮겼을 때의 super
deliver_order와 back_of_house를 함께 새 모듈 mod kitchen { ... } 안으로 옮기고, 그 밖에는 deliver_order가 없다고 합시다. fix_incorrect_order의 super::deliver_order()는 여전히 맞나요? crate::deliver_order()로 썼다면 어떻게 되나요?
해설 보기
여전히 맞습니다. super는 back_of_house의 부모를 가리키는데, 함께 옮겼으므로 그 부모는 이제 kitchen이고 거기에 deliver_order가 있습니다. crate::deliver_order()로 썼다면 크레이트 루트에서 찾는데 루트에는 더 이상 deliver_order가 없으므로 찾을 수 없다는 오류가 납니다. 서로 붙어 다니는 아이템 사이에서는 super가 옮길 때 고칠 곳을 줄여 줍니다.
3. 바깥에서 아침 식사 만들기
eat_at_restaurant 안에서 let meal = back_of_house::Breakfast { toast: String::from("Rye"), seasonal_fruit: String::from("apples") };라고 쓰면 컴파일되나요? 손님이 과일을 고를 수 없게 하면서 바깥에서 아침 식사를 만들 수 있게 하는 방법은 무엇인가요?
해설 보기
컴파일되지 않습니다. seasonal_fruit가 비공개 필드이므로 back_of_house 바깥에서 구조체 문법으로 값을 정할 수 없습니다. 바깥에서 만들 수 있게 하려면 summer처럼 공개 연관 함수를 두어, 빵만 인수로 받고 과일은 그 함수 안에서 정하게 합니다.
다음 글 use 키워드로 경로를 스코프 안으로 가져오기에서는 긴 경로를 매번 적지 않도록 use로 이름을 가져오는 방법과, pub use로 다시 내보내는 방법을 살펴봅니다.