Rust - Conditional Code Compilation
Rust'ta Koşullu Derleme: #[cfg] Niteliği ve cfg! Makrosu
Giriş
Rust, tek bir kod tabanından farklı platformlar ve yapılandırmalar için derleme yapmayı sağlayan güçlü bir koşullu derleme (conditional compilation) mekanizması sunar. Bu mekanizmanın merkezinde #[cfg] niteliği ve cfg! makrosu yer alır. Bu makale, her iki aracın nasıl çalıştığını, aralarındaki temel farkı ve ne zaman hangisinin kullanılacağını örnek bir kod üzerinden incelemektedir.
Örnek Kodun İncelenmesi
// This function only gets compiled if the target os is linux
#[cfg(target_os = "linux")]
fn are_you_on_linux() {
println!("You are running linux!");
}
// this function only gets compiled if the target os is not linux
#[cfg(not(target_os = "linux"))]
fn are_you_on_linux() {
println!("You are *not* running linux!");
}
pub fn execute_cfg_attribute() {
are_you_on_linux();
println!("Are you sure?");
// cfg! expression gives a boolean result
if cfg!(target_os = "linux") {
println!("Yes. It's definitely linux");
} else {
println!("Yes. It's definitely *not* linux!");
}
}
Linux Üzerinde Çıktı
You are running linux!
Are you sure?
Yes. It's definitely linux
Linux Dışında (örneğin Windows) Çıktı
You are *not* running linux!
Are you sure?
Yes. It's definitely *not* linux!
Adım Adım Analiz
#[cfg(target_os = "linux")]niteliği: İlkare_you_on_linuxfonksiyonu yalnızca hedef işletim sistemi Linux olduğunda derlenir. Diğer platformlarda bu fonksiyon kaynak koddan tamamen dışlanır; derleyici onu var olmayan bir kod gibi görür.#[cfg(not(target_os = "linux"))]niteliği: İkinci fonksiyon bunun tamamlayıcısıdır.not()koşulu, Linux dışındaki tüm platformlarda geçerli olur. Böylece iki fonksiyon aynı ismi taşısa da, her hedefte yalnızca biri mevcut olur ve isim çakışması yaşanmaz. Bu kalıp, platforma göre değişen API'ler için yaygın bir stratejidir.cfg!makrosu:execute_cfg_attributeiçindekiif cfg!(...)ifadesi farklı bir davranış gösterir.cfg!derleme zamanındatrueveyafalseüreten bir makrodur, ancak çevresindeki kod yine de derlenir. Derleyici, dalın koşulunu sabitle bırakır ve optimizasyon aşamasında ölü dalı eler.
Kritik Ayrım: Nitelik mi, Makro mu?
#[cfg] ile cfg! arasındaki fark, koşullu derlemenin ne zaman gerçekleştirildiğine dairdir:
#[cfg]derleme öncesi kod elemesi yapar. Elenen dal hiç derlenmez; içindeki sözdizimi hataları, tip hataları veya var olmayan bağımlılıklar hiçbir zaman derleyiciye ulaşmaz.cfg!yalnızca koşulun kendisini sabitle değerlendirir; iki dal da derlenir ve tip denetiminden geçer. Ertelenmiş elemeyi LLVM'nin optimizasyon aşaması yapar.
Pratik sonuç: Platforma özgü bir kod, hedefte mevcut olmayan bir API kullanıyorsa #[cfg] gerekir; aksi halde derleme başarısız olur. cfg! ise yalnızca değerin çalışma zamanı davranışını etkilemediği, kodun her platformda derlenebilir olduğu durumlara uygundur.
Yaygın cfg Koşulları
Koşullu derleme yalnızca işletim sistemiyle sınırlı değildir:
#[cfg(target_arch = "x86_64")] // hedef CPU mimarisi
#[cfg(feature = "serde")] // Cargo özelliği (feature)
#[cfg(test)] // yalnızca cargo test sırasında
#[cfg(debug_assertions)] // hata ayıklama derlemelerinde
#[cfg(all(unix, not(target_os = "macos")))]
fn linux_specific() {}
Özellikle #[cfg(test)], test modunun üretim derlemesine sızmasını engellediği için günlük Rust geliştirmenin temel taşlarından biridir. all(), any() ve not() işleçleri ise koşulların mantıksal olarak birleştirilmesini sağlar.
Sonuç
#[cfg] niteliği ve cfg! makrosu, Rust'ın koşullu derleme araçlarının tamamlayıcı iki yüzüdür. Nitelik, platforma özgü kodu derleme öncesinde eleyerek tek bir kod tabanından çoklu hedef desteği sağlar; makro ise her iki dalın da derlendiği durumlarda derleme zamanı bilgisini çalışma zamanı mantığına taşır. Platforma özgü API kullanımında #[cfg]'nin eleme davranışı zorunludur; basit dal ayrımında cfg! yeterlidir. Her iki araç birlikte, C'deki önişlemci makrolarının (Örn. #ifdef) sağladığı işlevi, tip güvenliğini ve modül hijyenini koruyarak karşılar.