En Rust, la compilación condicional es un mecanismo que permite incluir o excluir partes del código durante la fase de compilación basándose en criterios específicos. Esto es fundamental para desarrollar software multiplataforma, optimizar binarios para arquitecturas específicas o habilitar funcionalidades experimentales mediante features.
El atributo #[cfg(condición)] actúa como un filtro para el compilador. Si la condición es verdadera, el elemento inmediatamente inferior (una función, una estructura, un módulo, etc.) se incluye en el árbol de sintaxis abstracta (AST); de lo contrario, es como si nunca hubiera existido para el compilador. Si utilizas un tipo de dato dentro de una función, pero ese tipo fue excluido mediante un #[cfg], el compilador lanzará un error de “tipo no definido”.
El atributo #[cfg_attr(condición, atributo)] es una variante más versátil que permite aplicar un atributo de forma condicional. Por ejemplo, puede usarse para añadir #[derive(Debug)] a una estructura únicamente cuando se compila en modo de depuración, manteniendo el binario de producción más ligero y limpio.
Finalmente, para la lógica dentro de las expresiones (como un bloque if), se utiliza la macro cfg!. A diferencia de #[cfg], que excluye código, la macro cfg! devuelve un valor booleano (true o false) evaluado en tiempo de compilación, permitiendo que el flujo de control dependa de la configuración del entorno sin eliminar físicamente las ramas de código del binario resultante.
// 1. Uso de #[cfg] para definir funciones según el sistema operativo
#[cfg(target_os = "windows")]
fn get_platform() -> &'static str {
"Windows"
}
#[cfg(not(target_os = "windows"))]
fn get_platform() -> &'static str {
"Non-Windows (Unix/Linux/macOS)"
}
// 2. Uso de #[cfg_attr] para aplicar un atributo condicionalmente
// Solo implementamos Debug si estamos en modo debug (debug_assertions activas)
#[derive(PartialEq)]
#[cfg_attr(debug_assertions, Debug)]
struct SystemStatus {
name: &'static str,
is_active: bool,
}
fn main() {
// 3. Uso de la macro cfg! para lógica dentro de una expresión
if cfg!(test) {
println!("Ejecución en modo de prueba (test).");
} else {
println!("Ejecución en modo estándar.");
}
let status = SystemStatus {
name: get_platform(),
is_active: true,
};
// 4. Uso de operadores lógicos: all() y not()
// Se compila solo si es Unix Y NO es macOS
#[cfg(all(unix, not(target_os = "macos")))]
println!("Detectado: Sistema Unix que no es macOS.");
// 5. Manejo preventivo de errores con cfg_attr
// Como SystemStatus solo tiene Debug en modo debug, protegemos el print
#[cfg(debug_assertions)]
println!("Log de depuración: {:?}", status);
println!("Plataforma actual: {}", status.name);
}
Explicación del Código
- Funciones
get_platform: Se utilizan dos versiones de la misma función. La primera se incluye solo sitarget_oses"windows". La segunda utilizanot(target_os = "windows")para asegurar que, en cualquier otro sistema, la función esté disponible, evitando errores de símbolos no encontrados. - Estructura
SystemStatus: Utiliza#[cfg_attr(debug_assertions, Debug)]. Esto significa que el atributo#[derive(Debug)]solo se aplica si la compilación tiene las aserciones de depuración activas. En una compilación de producción (--release), la estructura no implementará el traitDebug. - Macro
cfg!(test): Dentro demain, la expresiónif cfg!(test)evalúa si se está ejecutando un test (cargo test). A diferencia de#[cfg(test)], la macro permite mantener el código dentro de la funciónmain, pero el resultado es evaluado estáticamente por el compilador. - Bloque
#[cfg(all(unix, not(target_os = "macos")))]: Utiliza operadores lógicos combinados. El compilador solo incluirá elprintln!si se cumplen ambas condiciones simultáneamente. - Bloque
#[cfg(debug_assertions)]: Este bloque es crítico para la integridad del código. Debido a queSystemStatusno tieneDebugen modo release (por elcfg_attrmencionado antes), intentar usarprintln!("{:?}", status)sin este bloque causaría un error de compilación en modo release. El#[cfg]asegura que la instrucción solo exista cuando el tipo también lo permite.
N° 129