Пакеты и крейты
Первые элементы системы модулей, которые мы рассмотрим, — это пакеты и крейты.
Крейт — это минимальный объём кода, который компилятор Rust рассматривает за один раз. Даже если вы запускаете rustc вместо cargo и передаёте один файл с исходным кодом (как мы делали в разделе «Написание и запуск программы на Rust» в главе 1), компилятор считает этот файл крейтом. Крейты могут содержать модули, и модули могут быть определены в других файлах, которые компилируются вместе с крейтом, как мы увидим в следующих разделах.
Крейт может быть одного из двух видов: бинарный крейт или библиотечный крейт.
Бинарные крейты — это программы, которые можно скомпилировать в исполняемый файл, например, программу для командной строки или сервер. Каждый такой крейт должен содержать функцию main, которая определяет, что происходит при запуске исполняемого файла. Все крейты, созданные нами до сих пор, были бинарными.
Библиотечные крейты не имеют функции main и не компилируются в исполняемый файл. Вместо этого они определяют функциональность, предназначенную для использования в нескольких проектах. Например, крейт rand, который мы использовали в главе 2, предоставляет функциональность для генерации случайных чисел. Чаще всего, когда разработчики Rust говорят «крейт», они имеют в виду библиотечный крейт и используют слово «крейт» как синоним общего понятия «библиотека» в программировании.
Корень крейта — это исходный файл, с которого начинает работу компилятор Rust и который составляет корневой модуль вашего крейта (мы подробно объясним модули в разделе «Определение модулей для управления областью видимости и доступом»)modules).
Пакет — это набор из одного или нескольких крейтов, предоставляющий определённый функционал. Пакет содержит файл Cargo.toml, который описывает, как собрать эти крейты. На самом деле, Cargo — это пакет, содержащий бинарный крейт для инструмента командной строки, который вы использовали для сборки кода. Пакет Cargo также содержит библиотечный крейт, от которого зависит бинарный крейт. Другие проекты могут зависеть от библиотечного крейта Cargo, чтобы использовать ту же логику, что и инструмент командной строки Cargo.
Пакет может содержать любое количество бинарных крейтов, но не более одного библиотечного крейта. Пакет должен содержать как минимум один крейт — библиотечный или бинарный.
Давайте разберём, что происходит при создании пакета. Сначала мы выполняем команду cargo new my-project:
$ cargo new my-project
Created binary (application) `my-project` package
$ ls my-project
Cargo.toml
src
$ ls my-project/src
main.rs
После выполнения cargo new my-project мы используем 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: каждый файл будет отдельным бинарным крейтом.