Как проверить, собран проект в debug или release режиме, используя cfg в Rust?
Основной способ: атрибут cfg(debug_assertions)
Вы уже на правильном пути. #[cfg(debug_assertions)] — это самый распространенный и идиоматичный способ проверки debug/release сборки в Rust. Давайте разберемся, как он работает и какие есть нюансы.
Как это работает:
debug_assertionsвключено по умолчанию при сборке в debug-режиме (командаcargo buildбез флагов).debug_assertionsотключено при сборке в release-режиме (cargo build --release).- Этот флаг также отключается при сборке с
--profile=test,--profile=benchи--profile=dev(но последний — это и есть debug).
Ваш пример абсолютно корректен. Однако есть один важный нюанс: тесты. Когда вы запускаете cargo test, код тестов компилируется в debug-режиме, поэтому debug_assertions будет включено. Если вы хотите, чтобы в тестах везде использовался release-код, вам нужно запускать cargo test --release.
Пример с функциями:
fn main() {
initialize_logging();
// ... остальной код
}
#[cfg(debug_assertions)]
fn initialize_logging() {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("debug"))
.init();
println!("Запуск в DEBUG режиме. Логирование на уровне DEBUG.");
}
#[cfg(not(debug_assertions))]
fn initialize_logging() {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("warn"))
.init();
println!("Запуск в RELEASE режиме. Логирование только WARN и ERROR.");
}
Альтернатива: #[cfg(profile = "release")] и #[cfg(profile = "debug")]
Этот подход использует более новую функциональность Rust, которая появилась в nightly-версии (на момент написания ответа). Он более явный и позволяет проверять конкретные профили, а не только debug_assertions.
Как это работает:
fn main() {
#[cfg(profile = "release")]
println!("Это release сборка, профиль: release");
#[cfg(profile = "debug")]
println!("Это debug сборка, профиль: debug");
#[cfg(profile = "test")]
println!("Это сборка для тестов, профиль: test");
}
Важное отличие: profile проверяет именно имя профиля, а не наличие debug_assertions. Это может быть полезно, если вы используете пользовательские профили (например, --profile=staging). Однако этот функционал пока нестабилен и требует включения #![feature(cfg_profile)] в вашем crate. Поэтому для production-кода рекомендуется использовать debug_assertions.
Правильный подход для production
Для большинства проектов используйте #[cfg(debug_assertions)] и #[cfg(not(debug_assertions))]. Это стабильно, предсказуемо и поддерживается всеми версиями Rust. Если вам нужно более тонкое управление (например, разные уровни логирования для разных профилей), рассмотрите использование крейта log с настройкой уровня через переменные окружения или конфигурационные файлы.
Полный пример с использованием крейта log:
use log::{info, warn, LevelFilter};
fn main() {
// Устанавливаем уровень логирования в зависимости от сборки
#[cfg(debug_assertions)]
let log_level = LevelFilter::Debug;
#[cfg(not(debug_assertions))]
let log_level = LevelFilter::Warn;
// Инициализируем логгер (например, simple_logger)
simple_logger::SimpleLogger::new()
.with_level(log_level)
.init()
.unwrap();
info!("Это информационное сообщение (будет в debug)");
warn!("Это предупреждение (будет везде)");
}
Такой подход считается идиоматичным и используется в популярных крейтах, таких как serde, tokio и других.
Использование пользовательских cfg-флагов через Cargo.toml
Если вам нужно больше контроля, чем дают стандартные debug_assertions или profile, вы можете определить собственные флаги условной компиляции в Cargo.toml. Это особенно полезно, если у вас есть несколько сред (development, staging, production) с разными настройками.
Пример настройки в Cargo.toml:
[features]
default = ["std"]
std = []
# Пользовательские флаги для разных режимов
[profile.dev]
rustflags = ["--cfg", "my_debug"]
[profile.release]
rustflags = ["--cfg", "my_release"]
Использование в коде:
fn main() {
#[cfg(my_debug)]
println!("Собрано с профилем dev (debug)");
#[cfg(my_release)]
println!("Собрано с профилем release");
// Если ни один из флагов не установлен
#[cfg(not(any(my_debug, my_release)))]
println!("Неизвестный профиль сборки");
}
Плюсы этого подхода:
- Полный контроль над флагами.
- Возможность комбинировать флаги (например,
my_debug+extra_logging). - Работает со всеми версиями Rust (стабильная).
Минусы:
- Требуется ручная настройка в Cargo.toml.
- Может конфликтовать с другими инструментами, которые ожидают стандартные флаги.
Этот способ стоит использовать, когда стандартных возможностей недостаточно — например, если вам нужно различать не только debug/release, но и другие режимы сборки (тесты, бенчмарки, документация).