Модули в Rust
До сих пор каждый пример жил в одном файле main.rs. Для маленьких программ это нормально, но настоящие проекты делят код на логические части. Для этого в Rust есть система модулей — mod, pub и use.
Объявляем модуль
Заголовок раздела «Объявляем модуль»Модуль — это именованный блок, который собирает связанный код вместе:
mod math { pub fn add(a: i32, b: i32) -> i32 { a + b }}
fn main() { let sum = math::add(2, 3); println!("{sum}");}Вывод:
5Обрати внимание на две вещи. Функция вызывается как math::add(...) — имя модуля служит путём. И она отмечена pub: без этого до неё нельзя было бы добраться снаружи модуля.
Всё по умолчанию приватное
Заголовок раздела «Всё по умолчанию приватное»Об этом стоит сказать отдельно, потому что многих это удивляет: содержимое модуля по умолчанию приватное (private). Если убрать pub из примера выше, код не скомпилируется:
mod math { fn add(a: i32, b: i32) -> i32 { // нет `pub` a + b }}
fn main() { let sum = math::add(2, 3); // ❌ ошибка: функция `add` приватная println!("{sum}");}Всё, чем должен пользоваться код снаружи модуля, нужно явно отметить pub. Так задумано: модуль может прятать свои внутренние вспомогательные функции и открывать только то, что задумано как публичный API.
Вложенные модули
Заголовок раздела «Вложенные модули»Модули могут содержать другие модули. Так большие проекты выстраивают древовидную структуру:
mod shapes { pub mod circle { pub fn area(radius: f64) -> f64 { 3.14159 * radius * radius } }}
fn main() { let area = shapes::circle::area(2.0); println!("{area}");}use — сокращаем длинные пути
Заголовок раздела «use — сокращаем длинные пути»Каждый раз писать shapes::circle::area(...) утомительно. use вводит путь в область видимости, и к нему можно обращаться напрямую:
mod shapes { pub mod circle { pub fn area(radius: f64) -> f64 { 3.14159 * radius * radius } }}
use shapes::circle;
fn main() { let area = circle::area(2.0); // короче, чем shapes::circle::area println!("{area}");}На самом деле ты уже пользовался use — это то же ключевое слово, что и на странице про HashMap (use std::collections::HashMap;).
Разносим модули по файлам
Заголовок раздела «Разносим модули по файлам»В примерах выше всё лежит в одном файле — этого требует Rust Playground. В настоящем проекте на твоём компьютере у каждого модуля обычно свой файл. Проект с модулем math обычно выглядит так:
src/├── main.rs└── math.rsmod math; // говорит Rust: код этого модуля ищи в math.rs
fn main() { let sum = math::add(2, 3); println!("{sum}");}pub fn add(a: i32, b: i32) -> i32 { a + b}mod math; (с точкой с запятой и без тела) говорит Rust загрузить содержимое модуля из файла math.rs в той же папке. Этим приёмом ты будешь пользоваться, как только проект перерастёт один файл.
Практика
Заголовок раздела «Практика»1. Модуль для температур
Заголовок раздела «1. Модуль для температур»Создай модуль convert с двумя публичными функциями: c_to_f (из Цельсия в Фаренгейт) и f_to_c (обратно). Из main выведи 100°C — это 212°F и 212°F — это 100°C.
Подсказка: формулы — c * 9.0 / 5.0 + 32.0 и (f - 32.0) * 5.0 / 9.0. Не забудь pub и вызывай функции через имя модуля, например convert::c_to_f(100.0).
Показать решение
mod convert { pub fn c_to_f(c: f64) -> f64 { c * 9.0 / 5.0 + 32.0 }
pub fn f_to_c(f: f64) -> f64 { (f - 32.0) * 5.0 / 9.0 }}
fn main() { println!("100°C — это {}°F", convert::c_to_f(100.0)); println!("212°F — это {}°C", convert::f_to_c(212.0));}Вывод:
100°C — это 212°F212°F — это 100°CОбе функции pub, поэтому main (который живёт снаружи модуля) может их вызвать. Убери один pub — и компилятор скажет, что функция приватная.
2. Приватная вспомогательная функция
Заголовок раздела «2. Приватная вспомогательная функция»Создай вложенный модуль school::grades с публичной функцией letter(score: u32) -> char, которая возвращает 'A' для 90 и выше, 'B' для 80 и выше и 'C' в остальных случаях. Внутри grades напиши ещё и приватную вспомогательную функцию is_at_least(score: u32, min: u32) -> bool, которой пользуется letter. Введи school::grades в область видимости через use и выведи оценку для 95, 85 и 70.
Подсказка: pub нужен и внутреннему mod grades в school, и fn letter, а у is_at_least его быть не должно. Код внутри того же модуля всегда может вызывать приватные функции.
Показать решение
mod school { pub mod grades { pub fn letter(score: u32) -> char { if is_at_least(score, 90) { 'A' } else if is_at_least(score, 80) { 'B' } else { 'C' } }
// приватная: вызывать её может только код внутри `grades` fn is_at_least(score: u32, min: u32) -> bool { score >= min } }}
use school::grades;
fn main() { for score in [95, 85, 70] { println!("{score} -> {}", grades::letter(score)); }}Вывод:
95 -> A85 -> B70 -> Cletter может вызвать is_at_least, потому что они живут в одном модуле. А из main вызов grades::is_at_least(95, 90) не сработает: компилятор скажет «function is_at_least is private» (функция is_at_least приватная). В этом и смысл: модуль показывает маленький публичный API и держит свои помощники при себе.
mod name { ... }собирает связанный код в именованный модуль.- Всё внутри модуля по умолчанию приватное — добавь
pub, чтобы открыть это снаружи. useвводит путь в область видимости, чтобы не писать его целиком каждый раз.- В проекте из нескольких файлов
mod name;(без тела) загружает код модуля из файла с тем же именем. - Код внутри модуля всегда может пользоваться его приватными элементами, так что держи помощники приватными и открывай только то, что нужно вызывающему коду.
Проверь себя
Заголовок раздела «Проверь себя»Проверь себя
1. Элементы внутри mod по умолчанию публичные или приватные?
2. Как сказать Rust загрузить код модуля из math.rs, а не писать его прямо на месте?
Результат: 0 / 2
Что почитать
Заголовок раздела «Что почитать»- Управление растущими проектами с помощью пакетов, крейтов и модулей — книга «Язык программирования Rust»: официальная глава о пакетах, крейтах и модулях и о том, как они связаны (перевод на русский).
- Определение модулей для управления областью действия и конфиденциальностью — книга «Язык программирования Rust»: разобранный пример дерева модулей и удобная шпаргалка по правилам (перевод на русский).
- Разделение модулей на разные файлы — книга «Язык программирования Rust»: подробнее о раскладке по файлам, в том числе о папках для вложенных модулей (перевод на русский).
- Модули — Rust на примерах: запускаемые примеры видимости,
use,superиself(перевод на русский). - Visibility and privacy — The Rust Reference (на английском): точные правила, включая
pub(crate)— «публично, но только внутри моего крейта».