Modules Structure

External link: https://doc.rust-lang.org/stable/book/ch07-02-defining-modules-to-control-scope-and-privacy.html

Правила работы с модулями

  • Crate Root. При компиляции crate, компилятор ищет корень: src/lib.rs для библиотеки, либо src/main.rs для запускаемого файла (binary);
  • Модули. При декларировании модуля в crate root, например, mod test, компилятор будет искать его код в одном из мест:
    • Сразу после объявления в том же файле (inline);
    • В файле src/test.rs;
    • В файл src/test/mod.rs - старый стиль, поддерживается, но не рекомендуется.
  • Подмодули. В любом файле, кроме crate root, можно объявить подмодуль, например, mod automa. Компилятор будет искать код подмодуля в одном из мест:
    • Сразу после объявления в том же файле (inline);
    • В файле src/test/automa.rs;
    • В файле src/test/automa/mod.rs - *старый стиль, поддерживается, но не рекомендуется.
  • Путь до кода. Когда модуль часть crate, можно обращаться к его коду из любого места этой crate в соответствии с правилами privacy и указывая путь до кода. Например, путь до типа robot в подмодуле automa будет crate::test::automa::robot;
  • Private/public. Код модуля скрыт от родительских модулей по умолчанию. Для его публикации нужно объявлять его с pub mod вместо mod;
  • Use. Ключевое слово use нужно для сокращения написания пути до кода: use crate::test::automa::robot позволяет далее писать просто robot для обращения к данным этого типа.
Note

Нужно лишь единожды внести внешний файл с помощью mod в дереве модулей, чтобы он стал частью проекта, и чтобы другие файлы могли на него ссылаться. В каждом файле с помощью mod не надо ссылаться, mod - это НЕ include из Python и других языков программирования.

Пример структуры

my_crate
├── Cargo.lock
├── Cargo.toml
└── src
    ├── test
    │   └── automa.rs
    ├── test.rs
    └── main.rs

Вложенные модули

Для примера, возьмём библиотеку, обеспечивающую работу ресторана. Ресторан делится на части front - обслуживание посетителей, и back - кухня, мойка, бухгалтерия.

mod front {
    mod hosting {
        fn add_to_waitlist() {}
        fn seat_at_table() {}
    }

    mod serving {
        fn take_order() {}
        fn serve_order() {}
        fn take_payment() {}
    }
}

Пример обращения к функции вложенного модуля

Объявление модуля публичным с помощью pub не делает его функции публичными. Нужно указывать публичность каждой функции по отдельности:

mod front_of_house {
    pub mod hosting { // модуль публичен, чтобы к нему обращаться
        pub fn add_to_waitlist() {} // функция явно публична
        // несмотря на публичность модуля, к функции обратиться нельзя
        // если она непублична
    }
}

pub fn eat_at_restaurant() {
    // Абсолютный путь через корень - ключевое слово crate
    crate::front_of_house::hosting::add_to_waitlist();

    // Относительный путь
    front_of_house::hosting::add_to_waitlist();
}

Обращние к функции выше уровнем

Относительный вызов функции можно сделать через super (аналог “..” в файловой системе):

fn deliver_order() {}

mod back_of_house {
    fn fix_incorrect_order() {
        cook_order();
        super::deliver_order(); // вызов функции в родительском модуле
    }
    fn cook_order() {}
}

Обращение к структурам и перечислениям

Поля структур приватны по умолчанию. Обозначение структуры публичной с pub не делает её поля публичными - каждое поле нужно делать публичным по отдельности.

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");
}

Поля перечислений публичны по умолчанию. Достатоно сделать само перечисление публичным pub enum, чтобы видеть все его поля.

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;
}

Обращение к объектам под другим именем

С помощью as можно создать ярлык на объект в строке с use. Особенно это удобно в случае, когда нужно обратиться к одинаковым по имени объектам в разных модулях:

use std::fmt::Result;
use std::io::Result as IoResult; // IoResult - ярлык на тип Result в модуле io

fn function1() -> Result {
    // ... }

fn function2() -> IoResult<()> {
    // ... }

Ре-экспорт объектов

При обращении к объекту с помощью use, сам ярлык этот становится приватным - через него могут обращаться только функции текущего scope. Для того, чтобы из других модулей функции могли тоже обратиться через этот ярлык, нужно сделать его публичным:

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    } }

pub use crate::front_of_house::hosting; // ре-экспорт объекта

pub fn eat_at_restaurant() {
    hosting::add_to_waitlist(); // обращение к функции через ярлык
}

Работа с внешними библиотеками

Внешние библиотеки включаются в файл Cargo.toml. Далее, публичные объекты из них заносятся в scope с помощью use.

# Cargo.toml
rand = "0.8.5"
use rand::Rng;
fn main() {
    let secret_number = rand::thread_rng().gen_range(1..=100);
}

Если нужно внести несколько объектов из одной библиотеки, то можно сократить количество use:

//use std::cmp::Ordering;
//use std::io;
use std::{cmp::Ordering, io}; // список объектов от общего корня

//use std::io;
//use std::io::Write;
use std::io{self, Write}; // включение самого общего корня в scope

use std::collections::*; // включение всех публичных объектов по пути
Warning

Следует быть осторожным с оператором glob - *, так как про внесённые с его помощью объекты сложно сказать, где именно они были определены.