Пользователь
0,0
рейтинг
19 января в 11:15

Разработка → Немного о модульной системе языка Rust из песочницы tutorial

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


В этой статье будет идти речь о самых основных способах использования модулей в данном языке (используемая ОС: 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 без скобочек.
               // Иначе будет ошибка: "error: expected one of `::`, `;`, or `as`, found `(` "
fn main() {
	println!("{} - 1",one());   // Вызываем функцию one() не используя num::number::
}

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

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



Литература:

The Rust Reference (английский)
The Rust Programming Language (английский)
The Rust Programming Language (русский)
Cargo Guide (английский)
@no_face
карма
12,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (6)

  • +2
    > use num::number::one; // Пишем one без скобочек.
    > Но так делать не рекомендуется, т.к. может возникнуть конфликт имён.

    Не припомню таких официальных рекомендаций. Вот что делать `use foo::*;` в большинстве ситуаций не стоит — это да)
    • 0
      Автор имеет ввиду не писать num::number::one().

      • +1
        Нет, автор имеет в виду не писать one(), а импортировать хотя бы последний неймспейс: number::one().
        • 0
          Что? Автор явно написал не писать `use num::number::one()`, а писать `use num::number::one`. Это видно из второй строчки комментария. Автор так же не слова про последний неймспейс не сказал и импортирует функцию, а не неймспейс.

          Правда, я не знаю кому придет в голову писать `use num::number::one()`
          • 0
            Это видно из второй строчки комментария.
            На самом деле вторая строчка там другая:
            use num::number::one; // Пишем one без скобочек.
                                  // Иначе будет ошибка: "error: expected one of `::`, `;`, or `as`, found `(` "
            
            Другими словами, рекомендаций на тему скобочек никаких быть не может: либо так, либо ошибка компиляции.
            Автор … импортирует функцию, а не неймспейс.
            Верно. После чего как раз и добавляет: но так делать не рекомендуется.
            Смотрите:
            Также можно импортировать саму функцию:

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

            Но так делать не рекомендуется, т.к может возникнуть конфликт имён.
  • 0
    С момента
    Теперь создаём новый контейнер, который будет хранить исполняемый файл

    становится непонятно, где нужно создавать модуль ENAME? Внутри папки LNAME? Или рядом с ней?
    Вы привели содержимое папки LNAME, почему после второго шага не сделать то же самое?)

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.