Материал: Язык программирования Rust

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

После того, как мы запустили cargo new
, мы используем ls
, чтобы увидеть, что создал
Cargo. В каталоге проекта есть файл Cargo.toml, дающий нам пакет. Также есть каталог src,
содержащий main.rs. Откройте Cargo.toml в текстовом редакторе и обратите внимание,
что в нём нет упоминаний о src/main.rs. Cargo следует соглашению о том, что src/main.rs
— это корневой модуль бинарного крейта с тем же именем, что и у пакета. Точно так же
Cargo знает, что если каталог пакета содержит src/lib.rs, пакет содержит библиотечный крейт с тем же именем, что и пакет, а src/lib.rs является корневым модулем этого крейта.
Cargo передаёт файлы корневого модуля крейта в rustc для сборки библиотечного или бинарного крейта.
Здесь у нас есть пакет, который содержит только src/main.rs, что означает, что он содержит только бинарный крейт с именем my-project
. Если пакет содержит src/main.rs и
src/lib.rs, он имеет два крейта: бинарный и библиотечный, оба с тем же именем, что и пакет. Пакет может иметь несколько бинарных крейтов, помещая их файлы в каталог
src/bin: каждый файл будет отдельным бинарным крейтом.
$
cargo new my-project
Created binary (application) `my-project` package
$
ls my-project
Cargo.toml src
$
ls my-project/src main.rs

Определение модулей для контроля видимости и
закрытости
В этом разделе мы поговорим о модулях и других частях системы модулей, а именно:
путях (paths), которые позволяют именовать элементы; ключевом слове use
, которое приносит путь в область видимости; ключевом слове pub
, которое делает элементы общедоступными. Мы также обсудим ключевое слово as
, внешние пакеты и оператор glob. А пока давайте сосредоточимся на модулях!
Во-первых, мы начнём со списка правил, чтобы вам было легче ориентироваться при организации кода в будущем. Затем мы подробно объясним каждое из правил.
Шпаргалка по модулям
Здесь мы даём краткий обзор того, как модули, пути, ключевое слово use и ключевое слово pub работают в компиляторе и как большинство разработчиков организуют свой код. В этой главе мы рассмотрим примеры каждого из этих правил, и это удобный момент чтобы напомнить о том, как работают модули.
Начнём с корня крейта: при компиляции компилятор сначала ищет корневой модуль крейта (обычно это src/lib.rs для библиотечного крейта или src/main.rs для бинарного крейта) для компиляции кода.
Объявление модулей: В файле корневого модуля крейта вы можете объявить новые модули; скажем, вы объявляете модуль “garden” с помощью mod garden;
Компилятор будет искать код модуля в следующих местах:
в этом же файле, между фигурных скобок, которые заменяют точку с запятой после mod garden в файле src/garden.rs
в файле src/garden/mod.rs
Объявление подмодулей: В любом файле, кроме корневого модуля крейта, вы можете объявить подмодули. К примеру, вы можете объявить mod vegetables;
в
src/garden.rs. Компилятор будет искать код подмодуля в каталоге с именем родительского модуля в следующих местах:
в этом же файле, сразу после mod vegetables
, между фигурных скобок, которые заменяют точку с запятой в файле src/garden/vegetables.rs
в файле src/garden/vegetables/mod.rs
Пути к коду в модулях: После того, как модуль станет частью вашего крейта и если допускают правила приватности, вы можете ссылаться на код в этом модуле из любого места вашего крейта, используя путь к коду. Например, тип
Asparagus
, в подмодуле vegetables модуля garden, будет найден по пути crate::garden::vegetables::Asparagus

Скрытие или общедоступность: Код в модуле по умолчанию скрыт от родительского модуля. Чтобы сделать модуль общедоступным, объявите его как pub mod вместо mod
. Чтобы сделать элементы общедоступного модуля тоже общедоступными, используйте pub перед их объявлением.
Ключевое слово
use
: Внутри области видимости использование ключевого слова use создаёт псевдонимы для элементов, чтобы уменьшить повторение длинных путей. В любой области видимости, в которой может обращаться к crate::garden::vegetables::Asparagus
, вы можете создать псевдоним use crate::garden::vegetables::Asparagus;
и после этого вам нужно просто писать
Asparagus
, чтобы использовать этот тип в этой области видимости.
Мы создали бинарный крейт backyard
, который иллюстрирует эти правила. Директория крейта, также названная как backyard
, содержит следующие файлы и директории:
Файл корневого модуля крейта в нашем случае src/main.rs, и его содержимое:
Файл: src/main.rs
Строка pub mod garden;
говорит компилятору о подключении кода, найденном в
src/garden.rs:
Файл: src/garden.rs
А здесь pub mod vegetables;
указывает на подключаемый код в src/garden/vegetables.rs.
Этот код:
backyard
├── Cargo.lock
├── Cargo.toml
└── src
├── garden
│ └── vegetables.rs
├── garden.rs
└── main.rs use crate::garden::vegetables::Asparagus; pub mod garden; fn main
() { let plant = Asparagus {}; println!
(
"I'm growing {:?}!"
, plant);
} pub mod vegetables;
#[derive(Debug)]
pub struct
Asparagus
{}

Теперь давайте рассмотрим детали этих правил и продемонстрируем их в действии!
Группировка связанного кода в модулях
Модули позволяют упорядочивать код внутри крейта для удобочитаемости и лёгкого повторного использования. Модули также позволяют нам управлять приватностью
элементов, поскольку код внутри модуля по умолчанию является закрытым. Частные элементы — это внутренние детали реализации, недоступные для внешнего использования. Мы можем сделать модули и элементы внутри них общедоступными, что позволит внешнему коду использовать их и зависеть от них.
В качестве примера, давайте напишем библиотечный крейт предоставляющий функциональность ресторана. Мы определим сигнатуры функций, но оставим их тела пустыми, чтобы сосредоточиться на организации кода, вместо реализации кода для ресторана.
В ресторанной индустрии некоторые части ресторана называются фронтом дома, а другие задней частью дома. Фронт дома это там где находятся клиенты; здесь размещаются места клиентов, официанты принимают заказы и оплаты, а бармены делают напитки. Задняя часть дома это где шеф-повара и повара работают на кухне,
работают посудомоечные машины, а менеджеры занимаются административной деятельностью.
Чтобы структурировать крейт аналогично тому, как работает настоящий ресторан,
можно организовать размещение функций во вложенных модулях. Создадим новую библиотеку (библиотечный крейт) с именем restaurant выполнив команду cargo new restaurant --lib
; затем вставим код из листинга 7-11 в src/lib.rs для определения некоторых модулей и сигнатур функций. Это секция фронта дома:
Файл: src/lib.rs mod front_of_house { mod hosting { fn add_to_waitlist
() {} fn seat_at_table
() {}
} mod serving { fn take_order
() {} fn serve_order
() {} fn take_payment
() {}
}
}

Листинг 7-1: Модуль
front_of_house
, содержащий другие модули, которые в свою очередь содержат
функции
Мы определяем модуль, начиная с ключевого слова mod
, затем определяем название модуля (в данном случае front_of_house
) и размещаем фигурные скобки вокруг тела модуля. Внутри модулей, можно иметь другие модули, как в случае с модулями hosting и
serving
. Модули также могут содержать определения для других элементов, таких как структуры, перечисления, константы, типажи или — как в листинге 7-1 — функции.
Используя модули, мы можем сгруппировать связанные определения вместе и сказать почему они являются связанными. Программистам будет легче найти необходимую функциональность в сгруппированном коде, вместо того чтобы искать её в одном общем списке. Программисты, добавляющие новые функции в этот код, будут знать, где разместить код для поддержания порядка в программе.
Как мы упоминали ранее, файлы src/main.rs и src/lib.rs называются корневыми модулями
крейта. Причина такого именования в том, что содержимое любого из этих двух файлов образует модуль с именем crate в корне структуры модулей крейта, известной как
дерево модулей.
В листинге 7-2 показано дерево модулей для структуры модулей, приведённой в коде листинга 7-1.
Листинг 7-2: Дерево модулей для для структуры модулей приведённой в коде в листинге 7-1
Это дерево показывает, как некоторые из модулей вкладываются друг в друга; например, hosting находится внутри front_of_house
. Дерево также показывает, что некоторые модули являются братьями (siblings) друг для друга, то есть они определены в одном модуле; hosting и serving это братья которые определены внутри front_of_house
Если модуль A содержится внутри модуля B, мы говорим, что модуль A является
*потомком * (child) модуля B, а модуль B является *родителем * (parent) модуля A.
Обратите внимание, что родителем всего дерева модулей является неявный модуль с именем crate
Дерево модулей может напомнить вам дерево каталогов файловой системы на компьютере; это очень удачное сравнение! По аналогии с каталогами в файловой системе, мы используется модули для организации кода. И так же, как нам надо искать файлы в каталогах на компьютере, нам требуется способ поиска нужных модулей.
crate
└── front_of_house
├── hosting
│ ├── add_to_waitlist
│ └── seat_at_table
└── serving
├── take_order
├── serve_order
└── take_payment

Пути для ссылки на элемент в дереве модулей
Чтобы показать Rust, где найти элемент в дереве модулей, мы используем путь так же,
как мы используем путь при навигации по файловой системе. Чтобы вызвать функцию,
нам нужно знать её путь.
Пути бывают двух видов:
абсолютный путь - это полный путь, начинающийся от корневого модуля крейта;
для кода из внешнего крейта абсолютный путь начинается с имени крейта, а для кода из текущего крейта он начинается с литерала crate
относительный путь начинается с текущего модуля и использует ключевые слова self
, super или идентификатор в текущем модуле.
Как абсолютные, так и относительные, пути состоят из одного или нескольких идентификаторов, разделённых двойными двоеточиями (
::
).
Вернёмся к листингу 7-1, скажем, мы хотим вызвать функцию add_to_waitlist
. Это то же самое, что спросить: какой путь у функции add_to_waitlist
? В листинге 7-3 мы немного упростили код листинга 7-1, удалив некоторые модули и функции.
Мы покажем два способа вызова функции add_to_waitlist из новой функции eat_at_restaurant
, определённой в корневом модуле крейта. Эти пути правильные, но остаётся ещё одна проблема, которая не позволит этому примеру скомпилироваться как есть. Мы скоро объясним почему.
Функция eat_at_restaurant является частью общедоступного API нашего библиотечного крейта, поэтому мы помечаем её ключевым словом pub
. В разделе "Раскрываем приватные пути с помощью ключевого слова pub
"
мы рассмотрим более подробно pub
Файл: src/lib.rs
Листинг 7-3. Вызов функции
add_to_waitlist
с использованием абсолютного и относительного пути
mod front_of_house { mod hosting { fn add_to_waitlist
() {}
}
} pub fn eat_at_restaurant
() {
// Absolute path crate::front_of_house::hosting::add_to_waitlist();
// Relative path front_of_house::hosting::add_to_waitlist();
}

При первом вызове функции add_to_waitlist из eat_at_restaurant мы используем абсолютный путь. Функция add_to_waitlist определена в том же крейте, что и eat_at_restaurant
, и это означает, что мы можем использовать ключевое слово crate в
начале абсолютного пути. Затем мы добавляем каждый из последующих дочерних модулей, пока не составим путь до add_to_waitlist
. Вы можете представить себе файловую систему с такой же структурой: мы указываем путь
/front_of_house/hosting/add_to_waitlist для запуска программы add_to_waitlist
;
использование имени crate в качестве корневого модуля крейта аналогично использованию
/
для указания корня файловой системы в вашей оболочке.
Второй раз, когда мы вызываем add_to_waitlist из eat_at_restaurant
, мы используем относительный путь. Путь начинается с имени модуля front_of_house
, определённого на том же уровне дерева модулей, что и eat_at_restaurant
. Для эквивалентной файловой системы использовался бы путь front_of_house/hosting/add_to_waitlist
Начало пути с имени модуля означает, что путь является относительным.
Выбор, использовать относительный или абсолютный путь, является решением, которое вы примете на основании вашего проекта. Решение должно зависеть от того, с какой вероятностью вы переместите объявление элемента отдельно от или вместе с кодом использующим этот элемент. Например, в случае перемещения модуля front_of_house и его функции eat_at_restaurant в другой модуль с именем customer_experience
, будет необходимо обновить абсолютный путь до add_to_waitlist
, но относительный путь всё
равно будет действителен. Однако, если мы переместим отдельно функцию eat_at_restaurant в модуль с именем dining
, то абсолютный путь вызова add_to_waitlist останется прежним, а относительный путь нужно будет обновить. Мы предпочитаем указывать абсолютные пути, потому что это позволяет проще перемещать определения кода и вызовы элементов независимо друг от друга.
Давайте попробуем скомпилировать код из листинга 7-3 и выяснить, почему он ещё не компилируется. Ошибка, которую мы получаем, показана в листинге 7-4.

1   ...   10   11   12   13   14   15   16   17   ...   62
Источник: https://files.student-it.ru/previewfile/264058