[Из песочницы] Немного о модульной системе языка Rust

Чуть-чуть о статье


В этой статье будет идти речь о самых основных способах использования модулей в данном языке (используемая ОС: Linux). Всем известно, что лучший способ запомнить информацию надолго — это предоставить её кому-либо в виде лекции. Для этого и была написана данная статья — чтобы помочь самому себе, но буду очень рад, если она поможет и тебе, читатель.

Корневой модуль


Давайте создадим контейнер (библиотеку) для хранения модулей (NAME — имя контейнера):

$ cargo new LNAME
$ cd LNAME/


И вот что мы получили в нашем контейнере LNAME:

        ./
        ../
        Cargo.toml
        src/
        |------  ./
        |------  ../
        '------  lib.rs


У всех наших модулей должен быть корневой модуль, которым является файл LNAME/src/lib.rs — он был создан автоматически. Последующие модули (суб-модули) будут «разрастаться» начитая с корневого модуля, как дерево. Мы обращаемся к суб-модулям с помощью нотации :: .В модулях мы будем хранить функции, которые возвращают строки. Пишем в файле lib.rs:

fn one() -> String {
        "one".to_string()     // Функция возвращает строку "one".
}


ОК. Теперь создаём новый контейнер, который будет хранить исполняемый файл (ENAME — имя контейнера для храниния исполняемого файла):

$ cargo new ENAME --bin
$ cd EMANE


Чтобы мы могли использовать наш корневой модуль, нам необходимо в файл Cargo.toml контейнера EMANE записать:


[lib]
name = "имя_для_обращения"


С помощью этого имени мы будем обращаться к нашему корневому модулю (далее вместо имя_для_обращения будет num). Мы как бы присваиваем нашему корневому модулю имя.

Продолжаем писать:

path = "путь_к_нашему_модулю" 


«путь_к_нашему_модулю» — это путь к модулю lib.rs относительно местоположения файла, который мы редактируем.

И пишем в main.rs следующее:

extern crate num;     // Импортируем контейнер num для получения функции one().

fn main() {
        println!("{} - 1",num::one());   // Вызываем функцию one() из модуля num.
}


И запускаем:

$ cargo run


Ошибка! Прочитав пояснение компилятора, вы можете сделать вывод, что функция one () приватна. Это значит, что мы не можем использовать её в своём main.rs, но можем использовать в lib.rs. И что нам делать? Поставим перед функцией слово pub:

pub fn one() ...         // Делаем функцию публичной.


И запускаем:

$ cargo run

Выход:

one - 1


Ура! Первый шаг сделан.

Всё, что стоит после num:: при вызовах функций должно быть публичным! Например:

num::newmod::oldmod::print_number()     // Вызов функции print_number()


Чтобы получить доступ к функции print_number () нам необходимо сделать все модули, находящиеся перед функцией, публичными…

Модуль в модуле


Давайте добавим модуль в корневой модуль и перенесём туда нашу функцию:

pub mod number {            //  Если хотите пользоваться функцией one(), то не забывайте ставить pub.
    pub fn one() -> String {
        "one".to_string()
    }   
}


Как вы заметили, для того, чтобы создать суб-модуль, необходимо воспользоваться словом mod (обязательно воспользуйтесь словом pub — без него нас не добраться до функции one ()).

Далее необходимо изменить файл main.rs:

extern crate num;

fn main() {
        println!("{} - 1",num::number::one());   // Вызываем функцию one() из модуля number, 
                                                                     // который находится в корневом модуле num.
}


И командуем:

$ cargo run


Выход:

one - 1


Ура! А теперь давайте попробуем объявить наш суб-модуль иначе. В lib.rs всё удаляем и пишем:

pub mod number;


Сохраняем. Если мы объявим модуль таким образом, то компилятор будет искать наш модуль либо в файле number.rs рядом с файлом, в котором находится такое объявление (в данном случае — src/number.rs), либо в файле mod.rs, который находится в папке number рядом с файлом, в котором находится такое объявление (в данном случае — src/number/mod.rs). При этом нам не надо заново объявлять модуль: это сделано при изначальном объявлении mod.!!!

Давайте рассмотрим оба варианта. Первый:

// Файл number.rs:

pub fn one() -> String {       //  Если хотите пользоваться функцией one(), то не забывайте ставить pub.
        "one".to_string()
}

// Файл main.rs(ничего не меняется):

extern crate num;

fn main() {
        println!("{} - 1",num::number::one());   // Вызываем функцию one() из модуля number, 
                                                                    // который находится в корневом модуле num.
}


Всё работает.

Второй вариант:

// Файл mod.rs:

pub fn one() -> String {  //  Если хотите пользоваться функцией one(), то не забывайте ставить pub.
        "one".to_string()
}


Файл main.rs — ничего не меняется.

Всё работает.

Используем use


Ключевое слово use позволяет импортировать модуль в нашу локальную область видимости.


Короче, если мы напишем так:

extern crate num;

use num::number;

fn main() {
        println!("{} - 1",number::one());   // Вызываем функцию one() из модуля number, 
                                                           // который находится в корневом модуле num.
}


То нам не потребуется писать полный путь при вызове: num: number: one (). Также можно импортировать саму функцию:

extern crate num;

use num::number::one;   // Пишем one без скобочек.

fn main() {
        println!("{} - 1",one());   // Вызываем функцию one() не используя num::number::
}


Но так делать не рекомендуется, т.к может возникнуть конфликт имён.

The End. Продолжение следует…

Литература:

The Rust Reference (английский)
The Rust Programming Language (английский)
The Rust Programming Language (русский)
Cargo Guide (английский)

© Habrahabr.ru