Легко понять модули Rust в нескольких файлах с помощью этого руководства
18 ноября 2022 г.TL;DR
- 💡 Легкое и простое объяснение модулей Rust в разных файлах.
- 🤿 Мы подробно рассмотрим реальный пример, чтобы изучить модульную систему.
- 📈 Множество диаграмм, которые помогут вам разобраться.
Система модулей в Rust может сбивать с толку разработчиков, использующих другие языки. Мне потребовалось некоторое время, чтобы понять это, поэтому я хочу поделиться с вами тем, как это работает и как легко организовать вашу программу в нескольких файлах.
Поехали.
Модули Rust в файлах
Rust требует, чтобы разработчики вручную создавали деревья модулей. Это можно сделать, объявив модули с помощью ключевого слова mod.
Дерево модулей начинается с корня крейта, обычно это src/lib.rs для крейта с библиотекой или src/main.rs для бинарного крейта. Компилятор Rust сначала ищет модули для компиляции в корне ящика.
Допустим, вы хотите импортировать модуль "a" в двоичный крейт, вы можете объявить модуль следующим образом:
main.rs
mod a;
fn main() { /* do amazing things */ }
Компилятор будет искать модуль в каталоге src в следующих местах:
В src/a.rs
.
├── Cargo.lock
├── Cargo.toml
└── src
├── a.rs
└── main.rs
Или в src/a/mod.rs
.
├── Cargo.lock
├── Cargo.toml
└── src
├── a
│ └── mod.rs
└── main.rs
Объявив mod a в main.rs, вы построили такое дерево модулей:
Подмодули Rust в файлах
В модуле вы можете создавать подмодули для дальнейшей организации кода. Допустим, вы хотите объявить модули "b" и "c" в модуле "a":
/src/a/mod.rs
mod b;
mod c;
Компилятор будет искать подмодули в каталоге src/a:
.
├── Cargo.lock
├── Cargo.toml
└── src
├── a
│ ├── b.rs
│ ├── c.rs
│ └── mod.rs
└── main.rs
Теперь вы построили такое дерево:
Видимость с "pub"
По умолчанию все элементы в модуле являются частными. Они видны только элементам в том же модуле.
источник/a/mod.rs
mod b;
mod c;
fn do_a() {} // only the other functions in module a can use it
// it's not visible to main.rs
Чтобы его родительские модули имели доступ к функции do_a, нам нужно добавить ключевое слово pub.
источник/a/mod.rs
pub fn do_a() {} // now it's visible to main.rs
Мы можем получить доступ к do_a, используя квалификатор пути < em>::.
источник/main.rs
mod a;
fn main() {
a::do_a();
}
Мы можем использовать тот же шаблон для подмодулей.
источник/a/b.rs
pub fn do_b() {} // visible to module "a" and all the submodules of module "a"
Добавив pub в do_b, функция теперь доступна для модуля "a".
источник/a/mod.rs
mod b;
mod c;
pub fn do_a {
b::do_b();
}
do_b также доступен для подмодулей модуля "c". Вы можете получить к нему доступ как по абсолютному, так и по относительному пути.
src/a/c.rs
pub fn do_c {
crate::a::b::do_b(); // absolute path
super::b::do_b(); // relative path
}
Реэкспорт элементов
Элемент подмодуля недоступен для неродительского модуля. Например, мы можем попытаться получить доступ к do_b в main.rs
.источник/main.rs
mod:a;
fn main() {
a::b::do_b();
// ^^^^ function `do_b` is private
}
Вы увидите сообщение об ошибке, в котором говорится, что do_b является частным. Это потому, что do_b пока доступен только в модуле "a". Чтобы сделать его видимым для корня ящика, нам нужно повторно экспортировать его, добавив pub в объявление модуля «b» из модуля «a».
источник/a/mod.rs
pub mod b;
// --snip--
Декларация об использовании
использование объявление может помочь вам сократить путь при доступе к элементу в другом модуле. Например, мы можем провести рефакторинг модуля "a":
источник/a/mod.rs
mod b;
mod c;
use b::do_b;
use c::do_c;
pub fn do_a {
do_b();
do_c();
}
Он создает привязку локального имени к своему пути для do_b и do_c. use очень полезно для длинных путей.
Пример из реальной жизни
Чтобы продемонстрировать модульную систему Rust, я создал простой интерфейс командной строки под названием affme, что означает "подтвердить меня".
affme — это генератор самоутверждения. Интерфейс командной строки принимает имя в качестве параметра и отображает рандомизированное подтверждение.
<цитата>Демонстрация доступна на GitHub. Не стесняйтесь взглянуть на репозиторий и попробовать✨
Дизайн кода прост:
В блоке "Формат"
* Требуется ввод данных пользователем, * объединяет ввод со случайным подтверждением и случайным смайликом, * применяет случайный цвет шрифта к объединенному утверждению, * и, наконец, выводит подтверждение. *
Чтобы продемонстрировать систему модулей в файлах, я разрабатываю дерево модулей следующим образом:
Несколько вещей, о которых стоит упомянуть:
* Этот пакет состоит из двух крейтов, одного бинарного и одной библиотеки. Я использую библиотечный крейт для инкапсуляции реализации и бинарный крейт для выполнения CLI. * В корневой папке библиотеки src/lib.rs он обращается к функциям из модулей аффирмации и formatter. * Модуль аффирмации и оба подмодуля модуля formatter используют одну и ту же функцию модуля random для случайного выбора элемента. Поскольку модуль аффирмации и подмодули formatter находятся в разных ветвях дерева, нам нужно объявить модуль random в общем предке модуля. дерево.
В файловой системе это выглядит так:
.
├── Cargo.lock
├── Cargo.toml
├── src
│ ├── affirmation.rs
│ ├── formatter
│ │ ├── color.rs
│ │ ├── emoji.rs
│ │ └── mod.rs
│ ├── lib.rs
│ ├── main.rs
│ └── random.rs
└── target
Давайте углубимся в корень библиотеки, чтобы увидеть, как устроен код.
источник/lib.rs
mod affirmation;
mod formatter;
mod random;
use affirmation::Affirmation;
use formatter::format;
pub fn affirm(name: &str) -> String {
let affirmation = Affirmation::new().random();
format(affirmation, name)
}
Здесь вы можете увидеть объявления модуля вверху. Вы также можете найти объявления use для создания привязки локального имени для Affirmation и format.
Случайный модуль прост:
источник/random.rs
use rand::Rng;
pub fn pick<'a, T: ?Sized>(items: &[&'a T]) -> &'a T {
let random_index: usize = rand::thread_rng().gen_range(0..items.len());
items.get(random_index).unwrap()
}
У него есть общедоступная функция pick, которая возвращает случайный элемент из фрагмента массива. Я использую эту функцию для выбора случайных утверждений, смайликов и цветов. В качестве примера рассмотрим модуль аффирмации:
src/аффирмация.rs
use crate::random;
#[derive(Debug)]
pub struct Affirmation<'a> {
affirmations: [&'a str; 6],
}
impl<'a> Affirmation<'a> {
pub fn new() -> Self {
let affirmations = [
"You're beautiful",
"You're awesome",
"You're wonderful",
"You've got this",
"You can do all things",
"Go get it",
];
Affirmation { affirmations }
}
pub fn random(&self) -> &'a str {
random::pick(&self.affirmations)
}
}
Вы можете увидеть объявление use для модуля random. Модуль аффирмации может получить доступ к модулю random, поскольку модуль random был объявлен в корневом каталоге библиотеки. Я использую ключевое слово pub в структуре Affirmation и ее функциях, чтобы корень ящика мог видеть их.
Вы можете найти тот же шаблон кодирования в подмодулях emoji и color.
Чтобы собрать все воедино, давайте взглянем на модуль format.
источник/formatter/mod.rs
mod color;
mod emoji;
use color::Color;
use colored::*;
use emoji::Emoji;
pub fn format(affirmation: &str, name: &str) -> String {
let emoji = Emoji::new();
let color = Color::new();
let phrase = format!("{}, {} {}", affirmation, name, emoji.random())
.color(color.random())
.bold()
.to_string();
format!(
"{}n{}n{}n{}n{}",
"*".repeat(phrase.len() + 2).magenta(),
format!("*{}*", " ".repeat(phrase.len())).magenta(),
format!(" ✏️ ...{} ", phrase,),
format!("*{}*", " ".repeat(phrase.len())).magenta(),
"*".repeat(phrase.len() + 2).magenta()
)
}
Он включает в себя подмодули color и emoji, поэтому мы можем объединить полное утверждение со случайными emoji и случайным цветом шрифта.
Заключительные мысли
Модули Rust в нескольких файлах немного отличаются от других языков, но как только вы поймете mod, use и pub, дизайн модуля становится проще и преднамереннее.
Памятка по модулю Rust
* Дерево модулей начинается с корня крейта. * Используйте mod, чтобы построить дерево с модулями и подмодулями. * Используйте pub, чтобы сделать элементы модуля видимыми для родительского модуля. * Вы можете реэкспортировать с помощью pub mod или pub use.
Ссылки
- Книга: Определение модулей для управления областью действия и конфиденциальностью — язык программирования Rust
- Книга: Использование объявлений — Справочник по Rust
- Книга: Items — The Rust Reference
- Книга: Модули — Справочник по Rust
- Книга: Пути — Справочник по Rust
- Статья: Как использовать модули Rust в разных файлах — Кейси Фальковски а>сильный>
- GitHub: репозиторий affme
Эта статья изначально была опубликована на веб-сайте Daw-Chih< /а>. н
Оригинал