Визначення модулів для контролю області видимості та приватності
В цьому розділі ми поговоримо про модулі та інші частини модульної системи, а саме: про шляхи, що дозволяють іменувати елементи; про ключове слово use
, яке додає шлях в область видимості; та про ключове слово 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. Компілятор шукатиме код підмодуля в теці з іменем батьківського модуля в наступних місцях:- Inline, directly following
mod vegetables
, within curly brackets instead of the semicolon - У файлі src/garden/vegetables.rs
- У файлі src/garden/vegetables/mod.rs
- Inline, directly following
- Шляхи до коду в модулях: Після того як модуль став частиною вашого крейту, ви можете звертатися до його коду з будь-якого місця даного крейту за допомогою шляху до коду, якщо дозволяють правила приватності. Наприклад, тип
Asparagus
в модулі garden vegetables буде знайдений за шляхомcrate::garden::vegetables::Asparagus
. - Приватність або публічність: Код всередині модуля є приватним від його батьківських модулів за замовчуванням. Аби зробити модуль публічним, оголосіть його за допомогою
pub mod
замістьmod
. Аби зробити елементи всередині публічного модуля публічними також, використовуйтеpub
перед їх оголошенням. - Ключове слово
use
: Всередині області видимості ключове словоuse
створює псевдоніми для елементів аби прибрати необхідність повторювати довгі шляхи. В будь якій області видимості, де необхідно звертатися до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. Його вміст:
Файл: src/main.rs
use crate::garden::vegetables::Asparagus;
pub mod garden;
fn main() {
let plant = Asparagus {};
println!("I'm growing {:?}!", plant);
}
The pub mod garden;
line tells the compiler to include the code it finds in src/garden.rs, which is:
Filename: src/garden.rs
pub mod vegetables;
Тут pub mod vegetables;
означає, що код в src/garden/vegetables.rs також буде підключений. Цей код:
#[derive(Debug)]
pub struct Asparagus {}
Тепер давайте розглянемо ці правила детальніше і продемонструємо їх в роботі!
Групування повʼязаного коду в модулі
Модулі дозволяють організувати код в крейті для читабельності та простоти повторного використання. Модулі також дозволяють контролювати приватність елементів, оскільки код всередині модуля є приватним за замовчуванням. Приватні елементи являють собою внутрішні деталі реалізації, недоступні для використання ззовні. Ми можемо зробити модулі і елементи всередині них публічними, що дозволить сторонньому коду використовувати їх і залежати від них.
В якості прикладу давайте напишемо бібліотечний крейт, що реалізує функціонал ресторану. Ми визначимо сигнатури функцій, проте залишимо їх вміст пустим для того, щоб сконцентруватися на організації коду, а не на деталях імплементації ресторану.
В ресторанній справі вирізняють такі частини ресторану як внутрішня кухня (back of house) та зал (front of house). Зал це те, де сидять відвідувачі. Тут знаходяться місця для клієнтів, офіціанти приймають замовлення і оплату, а бармени роблять напої. Внутрішня кухня - це те, де шеф-кухарі і повари працюють на кухні, посудомийники миють посуд, а менеджери виконують адміністративну роботу.
Для того аби структурувати наш крейт правильним чином, можемо організувати його функції у вкладених модулях. Створіть нову бібліотеку з іменем restaurant
, виконавши cargo new restaurant --lib
; тоді наберіть код з Лістинга 7-1 в src/lib.rs аби визначити деякі модулі та сигнатури функцій. Далі йде секція для зали:
Файл: src/lib.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() {}
}
}
Ми визначаємо модуль за допомогою ключового слова 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
. Дерево також показує, що деякі модулі є братами (siblings) один для одного, що означає, що вони визначені в одному модулі. hosting
та serving
є братами, визначеними всередині front_of_house
. Якщо модуль A міститься всередині модуля B, ми кажемо, що модуль A є нащадком (child) модуля B і що модуль B є батьком (parent) модуля A. Зверніть увагу, що батьком усього дерева модулів є неявний модуль з назвою crate
.
Дерево модулів може нагадувати вам дерево тек і файлів файлової системи на вашому компʼютері. Це дуже влучне порівняння! Ви можете використовувати модулі для організації коду точно так само, як ви використовуєте теки у файловій системі. І так само, як у випадку з файлами в теці, нам потрібен спосіб пошуку необхідних модулів.