Как проверить, собран проект в режиме release или debug, используя cfg в Rust?
Использование #[cfg(debug_assertions)] и #[cfg(not(debug_assertions))]
В Rust нет встроенного флага debug или release напрямую для условной компиляции. Вместо этого используется ключевое слово debug_assertions. Оно автоматически включено, когда компилятор не оптимизирует код (то есть в режиме debug по умолчанию) и отключено при оптимизации (режим release). Это самый правильный и идиоматичный способ.
Вот как это работает на практике:
fn main() {
let x = 42;
println!("x = {}", x);
// Этот блок кода будет скомпилирован ТОЛЬКО в debug сборке
if cfg!(debug_assertions) {
println!("[DEBUG] x is: {}", x);
println!("[DEBUG] Сейчас выполняется отладочная сборка!");
}
// Альтернативный вариант с атрибутом для целой функции
#[cfg(debug_assertions)]
{
println!("[DEBUG] Ещё один отладочный вывод");
// Здесь можно делать много действий
}
}
Обратите внимание на разницу между cfg!() (макрос, возвращающий bool) и #[cfg()] (атрибут, который полностью исключает код из компиляции).
cfg!(debug_assertions)— вычисляется во время компиляции и возвращаетtrueилиfalse. Код внутриifостаётся в бинарнике, но не выполняется, если условие ложно. Однако компилятор обычно оптимизирует «мёртвый» код, так что это безопасно.#[cfg(debug_assertions)]— атрибут, который полностью удаляет элемент (строку, блок, функцию) из сборки, если условие не выполнено. Это более агрессивный подход, который гарантирует, что код не попадёт в release-версию.
Проверка на release сборку
Если вам нужно сделать что-то только в release, используйте отрицание:
#[cfg(not(debug_assertions))]
fn release_only_function() {
println!("Этот код только в release сборке");
}
Как это проверить?
Запустите обычную сборку:
cargo run
# Вы увидите отладочные сообщения
Затем release сборку:
cargo run --release
# Отладочные сообщения исчезнут
Важный нюанс: пользовательские профили
Если вы создали свой профиль сборки в Cargo.toml (например, [profile.release] или [profile.dev]), то debug_assertions всё равно будет работать по умолчанию. Но будьте осторожны: если вы вручную отключите debug-assertions в debug профиле (что странно, но возможно), то код перестанет компилироваться в debug. Обычно это не нужно.
Также есть более продвинутый способ — использовать пользовательские флаги cfg через build.rs или переменные окружения, но для 99% задач debug_assertions — это именно то, что нужно.
Итог: используйте cfg!(debug_assertions) для простых проверок внутри функций и #[cfg(debug_assertions)] для целых блоков или отдельных функций. Это стандартный и документированный способ в Rust.
Альтернативный подход: использование build.rs и пользовательских флагов
Если вам нужно более гибкое управление, чем просто debug/release (например, три режима: debug, release и профилирование), вы можете задать собственные флаги условной компиляции через скрипт сборки build.rs.
Создайте файл build.rs в корне вашего проекта:
fn main() {
// Проверяем, собирается ли проект с профилем "release"
if std::env::var("PROFILE").unwrap() == "release" {
println!("cargo:rustc-cfg=my_release");
} else {
println!("cargo:rustc-cfg=my_debug");
}
}
Теперь в коде вы можете использовать свои флаги:
#[cfg(my_debug)]
fn debug_function() {
println!("Работает только в debug (по нашему определению)");
}
#[cfg(my_release)]
fn release_function() {
println!("Работает только в release");
}
Плюсы: полный контроль, можно добавить любые условия (например, feature = "verbose").
Минусы: нужно писать и поддерживать build.rs, что избыточно для простой задачи debug/release. Для большинства случаев лучше использовать встроенный debug_assertions.
Также помните, что переменная PROFILE может быть не единственной; вы можете проверять OPT_LEVEL (уровень оптимизации) или DEBUG (включены ли отладочные символы).