Пакеты и крейты

Первые части модульной системы, которые мы рассмотрим - это пакеты и крейты. Крейт - это исполняемый файл или библиотека. Выделяют два типа крейтов: библиотечный и исполняемый. Библиотечные крейты можно подключать в другие крейты, но нельзя исполнять. Исполняемые же крейты - полная противоположность библиотечным - могут исполняться, но их нельзя подключить в другие крейты. Корень крейта - это исходный файл, на котором запускается и, исходя из которого, составляет корневой модуль вашего крейта Rust компилятор (мы расскажем о корневых модулях в разделе "Определение модулей для управления областью видимости и конфиденциальностью"). Пакет состоит из одного или нескольких крейтов, которые предоставляют набор функций. Пакет содержит файл Cargo.toml описывающий, как собрать крейты пакета.

Несколько правил определяют что может содержать пакет. Сейчас нам достаточно знать следующие:
1 - пакет должен содержать ноль или один библиотечный крейт (library crate), не больше,
2 - пакет может содержать любое число исполняемых крейтов (binary crates),
3 - пакет должен содержать по крайней мере один крейт (библиотечный или исполняемый).

Давайте пройдёмся по тому, что происходит, когда мы создаём пакет. Сначала введём команду cargo new:

$ cargo new my-project
     Created binary (application) `my-project` package
$ ls my-project
Cargo.toml
src
$ ls my-project/src
main.rs

После ввода команды, Cargo создал файл Cargo.toml, предоставив пакет. Если мы просмотрим содержимое 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: каждый файл будет отдельным исполняемым крейтом.

Крейт группирует в области видимости связанные вместе функциональности, поэтому функциональности легко распространить между несколькими проектами. Например, rand крейт, который мы использовали в Главе 2 обеспечивает функциональность генерации случайных чисел. Можно использовать эту функциональность в наших собственных проектах, привнося крейт rand в область видимости проекта. Вся функциональность предоставляемая крейтом rand станет доступна через имя крейта rand.

Сохранение функциональности крейта в его собственной области видимости проясняет, является ли конкретная функциональность определённой в нашем крейте или в крейте rand, таким образом предотвращая потенциальные конфликты. Например, крейт rand предоставляет типаж с именем Rng. Мы также можем определить struct с именем Rng в нашем собственном крейте. Так как функциональность крейта находится в пространстве имён собственной области видимости, то когда мы добавляем rand как зависимость, компилятор не смущён ссылкой на имя Rng. В нашем крейте ссылка относится к объявленной у нас struct Rng. А доступ к типажу Rng из крейта rand мы бы получили как rand::Rng.

Давайте будем двигаться дальше и поговорим о модульной системе!