Визначення модулів для керування областю видимості та приватністю (Defining Modules to Control Scope and Privacy)
У цьому розділі ми поговоримо про модулі та інші частини системи модулів,
а саме шляхи (paths), які дають змогу називати елементи; ключове слово use, яке
вводить шлях (path) в область видимості; і ключове слово pub, щоб робити елементи
публічними. Ми також обговоримо ключове слово as, зовнішні пакети та glob-оператор.
Шпаргалка з модулів
Перш ніж ми перейдемо до деталей модулів і шляхів, тут ми наводимо коротку
довідку про те, як модулі, шляхи, ключове слово use і ключове слово pub
працюють у компіляторі, і як більшість розробників організовують свій код. Ми
розглядатимемо приклади кожного з цих правил протягом цього розділу, але це
чудове місце, куди можна звернутися як до нагадування про те, як працюють модулі.
- Починайте з кореня крейту: Під час компіляції крейта компілятор спочатку шукає в файлі кореня крейту (зазвичай src/lib.rs для бібліотечного крейта і src/main.rs для бінарного крейта) код для компіляції.
- Оголошення модулів: У файлі кореня крейта ви можете оголошувати нові модулі;
скажімо, ви оголошуєте модуль “garden” за допомогою
mod garden;. Компілятор шукатиме код модуля в таких місцях:- Вбудовано, всередині фігурних дужок, які замінюють крапку з комою після
mod garden - У файлі src/garden.rs
- У файлі src/garden/mod.rs
- Вбудовано, всередині фігурних дужок, які замінюють крапку з комою після
- Оголошення підмодулів: У будь-якому файлі, окрім кореня крейту, ви можете
оголошувати підмодулі. Наприклад, ви можете оголосити
mod vegetables;у src/garden.rs. Компілятор шукатиме код підмодуля всередині каталогу, названого на честь батьківського модуля, у таких місцях:- Вбудовано, безпосередньо після
mod vegetables, у фігурних дужках замість крапки з комою - У файлі src/garden/vegetables.rs
- У файлі src/garden/vegetables/mod.rs
- Вбудовано, безпосередньо після
- Шляхи (paths) до коду в модулях: Після того як модуль став частиною вашого крейта,
ви можете посилатися на код у цьому модулі з будь-якого іншого місця в тому
самому крейті, якщо правила приватності це дозволяють, використовуючи шлях (path) до
коду. Наприклад, тип
Asparagusу модулі garden vegetables буде знайдено за адресоюcrate::garden::vegetables::Asparagus. - Приватне проти публічного: Код у межах модуля за замовчуванням є приватним
для його батьківських модулів. Щоб зробити модуль публічним, оголосіть його з
pub modзамістьmod. Щоб зробити елементи всередині публічного модуля також публічними, використовуйтеpubперед їхніми оголошеннями. - Ключове слово
use: В межах області видимості ключове словоuseстворює скорочення для елементів, щоб зменшити повторення довгих шляхів (paths). У будь-якій області видимості, яка може посилатися наcrate::garden::vegetables::Asparagus, ви можете створити скорочення за допомогоюuse crate::garden::vegetables::Asparagus;, і відтоді вам потрібно буде лише писатиAsparagus, щоб використовувати цей тип у цій області видимості.
Тут ми створюємо бінарний крейт з назвою backyard, який ілюструє ці правила.
Каталог крейта, який також називається backyard, містить такі файли та
каталоги:
backyard
├── Cargo.lock
├── Cargo.toml
└── src
├── garden
│ └── vegetables.rs
├── garden.rs
└── 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 garden; каже компілятору включити код, який він знаходить у
src/garden.rs, а саме:
pub mod vegetables;
Тут pub mod vegetables; означає, що код у src/garden/vegetables.rs
також включається. Цей код такий:
#[derive(Debug)]
pub struct Asparagus {}
Тепер давайте перейдемо до деталей цих правил і продемонструємо їх у дії!
Групування пов’язаного коду в модулях
Модулі дають змогу нам організовувати код у межах крейта для зручності читання та простого повторного використання. Модулі також дають змогу нам керувати приватністю елементів, оскільки код у модулі за замовчуванням є приватним. Приватні елементи — це внутрішні деталі реалізації, недоступні для зовнішнього використання. Ми можемо вибрати зробити модулі та елементи всередині них публічними, що відкриває їх, щоб зовнішній код міг використовувати їх і залежати від них.
Як приклад, давайте напишемо бібліотечний крейт, який надає функціональність ресторану. Ми визначимо сигнатури функцій, але залишимо їхні тіла порожніми, щоб зосередитися на організації коду, а не на реалізації ресторану.
У ресторанній індустрії деякі частини ресторану називають front of house, а інші — back of house. Front of house — це місце, де перебувають клієнти; це охоплює те, де хости саджають клієнтів, офіціанти приймають замовлення та оплату, а бармени готують напої. Back of house — це місце, де шеф-кухарі та кухарі працюють на кухні, посудомийники прибирають, а менеджери виконують адміністративну роботу.
Щоб структурувати наш крейт у такий спосіб, ми можемо організувати його функції
у вкладені модулі. Створіть новий бібліотечний крейт під назвою restaurant,
виконавши cargo new restaurant --lib. Потім внесіть код з Лістингу 7-1 у
src/lib.rs, щоб визначити деякі модулі та сигнатури функцій; цей код є
розділом front of house.
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() {}
}
}
Ми визначаємо модуль за допомогою ключового слова mod, після якого йде назва
модуля (у цьому випадку front_of_house). Тіло модуля потім розміщується
всередині фігурних дужок. Усередині модулів ми можемо розміщувати інші модулі,
як у цьому випадку з модулями hosting і serving. Модулі також можуть
містити оголошення інших елементів, таких як структури, перелічення, константи,
трейти і, як у Лістингу 7-1, функції.
Використовуючи модулі, ми можемо групувати пов’язані оголошення разом і називати, чому вони пов’язані. Програмісти, які використовують цей код, можуть пересуватися кодом на основі груп, а не читати всі оголошення, що полегшує пошук потрібних їм оголошень. Програмісти, які додають нову функціональність до цього коду, знатимуть, куди розмістити код, щоб зберегти програму організованою.
Раніше ми згадували, що src/main.rs і src/lib.rs називаються коренями
крейту. Причина їхньої назви в тому, що вміст будь-якого з цих двох файлів
утворює модуль із назвою crate у корені модульної структури крейта, відомої як
дерево модулів.
Лістинг 7-2 показує дерево модулів для структури в Лістингу 7-1.
crate
└── front_of_house
├── hosting
│ ├── add_to_waitlist
│ └── seat_at_table
└── serving
├── take_order
├── serve_order
└── take_payment
Це дерево показує, як деякі модулі вкладені в інші модулі; наприклад,
hosting вкладений у front_of_house. Дерево також показує, що деякі модулі
є сусідніми, тобто вони визначені в одному модулі; hosting і serving
є сусідніми, визначеними всередині front_of_house. Якщо модуль A міститься
всередині модуля B, ми кажемо, що модуль A є дочірнім модулю B, а модуль B є
батьківським модулю A. Зверніть увагу, що все дерево модулів має корінь під
неявним модулем із назвою crate.
Дерево модулів може нагадати вам дерево каталогів файлової системи на вашому комп’ютері; це дуже влучне порівняння! Так само як каталоги у файловій системі, ви використовуєте модулі для організації свого коду. І так само як файли в каталозі, нам потрібен спосіб знаходити наші модулі.