Как получить метаданные Cargo-пакета (версию, имя, описание) в программе на Rust?
Использование макроса env!() для доступа к метаданным Cargo
Проблема, с которой вы столкнулись, очень распространена. К счастью, в Rust есть элегантное встроенное решение — макрос env!(). Во время компиляции Cargo передаёт множество переменных окружения, включая CARGO_PKG_VERSION, CARGO_PKG_NAME и другие. Макрос env!() позволяет считать эти переменные на этапе компиляции и встроить их значения прямо в бинарный файл.
Вот как это работает:
fn main() {
let name = env!("CARGO_PKG_NAME");
let version = env!("CARGO_PKG_VERSION");
let description = env!("CARGO_PKG_DESCRIPTION");
let authors = env!("CARGO_PKG_AUTHORS");
let repository = env!("CARGO_PKG_REPOSITORY");
println!("{} v{}", name, version);
if !description.is_empty() {
println!("{}", description);
}
println!("Авторы: {}", authors);
if !repository.is_empty() {
println!("Репозиторий: {}", repository);
}
}
Этот код будет работать всегда — и при запуске из исходников, и после установки через cargo install, и в Docker-контейнере. Значения подставляются компилятором на этапе сборки.
Полный список доступных переменных Cargo
Cargo предоставляет следующие переменные окружения для доступа к метаданным пакета:
CARGO_PKG_NAME— имя пакета из[package].nameCARGO_PKG_VERSION— полная версия (например, "1.2.3")CARGO_PKG_VERSION_MAJOR— только мажорная версия (1)CARGO_PKG_VERSION_MINOR— только минорная версия (2)CARGO_PKG_VERSION_PATCH— только патч-версия (3)CARGO_PKG_VERSION_PRE— pre-release метка (если есть, иначе пустая строка)CARGO_PKG_AUTHORS— авторы, разделённые двоеточиемCARGO_PKG_DESCRIPTION— описание из Cargo.tomlCARGO_PKG_HOMEPAGE— URL домашней страницыCARGO_PKG_REPOSITORY— URL репозиторияCARGO_PKG_LICENSE— лицензияCARGO_PKG_RUST_VERSION— минимальная версия RustCARGO_CRATE_NAME— имя крейта (может отличаться от имени пакета)
Обработка отсутствующих полей
Макрос env!() вызовет ошибку компиляции, если переменная не задана. Для опциональных полей (например, описание или репозиторий могут быть пустыми) используйте option_env!():
fn main() {
let name = env!("CARGO_PKG_NAME");
let version = env!("CARGO_PKG_VERSION");
let description = option_env!("CARGO_PKG_DESCRIPTION")
.unwrap_or("Без описания");
println!("{} v{} — {}", name, version, description);
}
Продвинутый пример: структура для метаданных
Для больших проектов можно создать отдельную структуру:
struct PackageInfo {
name: &'static str,
version: &'static str,
description: Option<&'static str>,
authors: Vec<&'static str>,
}
impl PackageInfo {
fn from_cargo() -> Self {
Self {
name: env!("CARGO_PKG_NAME"),
version: env!("CARGO_PKG_VERSION"),
description: option_env!("CARGO_PKG_DESCRIPTION"),
authors: env!("CARGO_PKG_AUTHORS")
.split(':')
.collect(),
}
}
fn print_version(&self) {
println!("{} v{}", self.name, self.version);
}
}
fn main() {
let info = PackageInfo::from_cargo();
info.print_version();
}
Этот подход гарантирует, что метаданные всегда актуальны и соответствуют вашему Cargo.toml. Никакого хардкода и проблем с чтением файлов во время выполнения!
Альтернативный подход: крейт built для расширенных метаданных
Если вам нужно больше, чем просто поля из Cargo.toml — например, хэш коммита Git, дата сборки или информация о зависимостях — рекомендую использовать крейт built.
Он работает через build script (build.rs) и генерирует Rust-файл с метаданными на этапе компиляции:
// build.rs
fn main() {
built::write_built_file().expect("Не удалось сгенерировать метаданные");
}
Затем в основном коде:
// main.rs
include!(concat!(env!("OUT_DIR"), "/built.rs"));
fn main() {
println!("Версия: {}", built::PKG_VERSION);
println!("Git commit: {}", built::GIT_COMMIT_HASH.unwrap_or("неизвестно"));
println!("Время сборки: {}", built::BUILT_TIME_UTC);
}
Плюсы: можно получить Git-хэш, информацию о зависимостях, флаги компиляции.
Минусы: требуется build script, что усложняет сборку. Для простого получения версии из Cargo.toml макрос env!() — более лёгкое и быстрое решение.
Выбирайте подход в зависимости от ваших потребностей: env!() для базовых метаданных, built для расширенной информации о сборке.