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

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

То что может содержать пакет определяют несколько правил. Пакет должен содержать ни одной или одну крейт библиотеку, и не более. Он может содержать столько исполняемых крейтов, сколько хотите, но он должен содержать по крайней мере один крейт (библиотечный или бинарный).

Давайте пройдёмся по тому, что происходит, когда мы создаём пакет. Сначала введём команду 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.

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