Пакеты и крейты
Первыми из частей системы модулей мы разберём пакеты и крейты.
Крейт (crate) — это минимальный объём кода, который компилятор Rust
рассматривает за один раз. Даже если запустить не cargo, а rustc и передать
ему один файл с исходным кодом (как мы делали ещё в разделе «Основы программы
на Rust» главы 1), компилятор посчитает этот файл
крейтом. Крейт может содержать модули, а модули могут быть определены в
других файлах, которые компилируются вместе с крейтом, — это мы увидим в
следующих разделах.
Крейты бывают двух видов: бинарные и библиотечные. Бинарные крейты — это
программы, которые компилируются в исполняемый файл и запускаются: например,
утилита командной строки или сервер. В каждом из них должна быть функция
main, определяющая, что происходит при запуске. Все крейты, которые мы
создавали до сих пор, были бинарными.
У библиотечных крейтов нет функции main, и они не компилируются в
исполняемый файл. Вместо этого они содержат функциональность, которой можно
пользоваться из разных проектов. Например, крейт rand, который мы
использовали в главе 2, умеет генерировать случайные
числа. Говоря «крейт», растацеаны чаще всего имеют в виду именно
библиотечный крейт и используют слово «крейт» как синоним общепрограммистского
понятия «библиотека».
Корень крейта (crate root) — это исходный файл, с которого компилятор Rust начинает работу и который образует корневой модуль крейта (подробно модули разберём в разделе «Модули: область видимости и приватность»).
Пакет (package) — это набор из одного или нескольких крейтов, реализующий некоторую функциональность. В пакете есть файл 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. В папке
my-project есть файл 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: каждый файл станет отдельным бинарным крейтом.