Rust 的 cfg 屬性

Rust 的 #[cfg()] 屬性是一個強大的功能,它允許開發人員在編譯時根據指定的條件有條件地編譯代碼。它支持在編譯過程中對代碼進行細粒度控制,從而產生更高效和優化的程序。

在其核心,#[cfg()] 是一個編譯時屬性宏 (與類函數宏相反)。它允許 Rust 在編譯期間計算謂詞並決定是否包含或排除特定的代碼塊或項。我們將通過多個示例介紹如何表達謂詞以及在何處使用此屬性。

條件

Rust 提供了幾個內置選項,可以與 #[cfg()] 一起使用,以定義哪些條件可以允許將代碼包含在構建中。

目標操作系統

#[cfg(target_os = "android")]
fn connect_to_instant_apps() {
    // ...
}

目標架構

#[cfg(target_arch = "x86_64")]
fn target_system_config() -> Option<SystemConfig> {
    // ...
}

功能標誌

使用功能選項,開發人員可以根據是否啓用特定的功能標誌有條件地編譯代碼。這在構建帶有可選特性的庫或應用程序時特別有用。

#[cfg(feature = "server")]
fn render(nodes: Vec<Node>) -> impl View {
    // ...
}

調試模式

debug_assertions 選項允許以調試模式選擇性地編譯代碼。

#[cfg(debug_assertions)]
fn debug_payload(payload: &Payload) {
    // Code runs only in debug mode
}

測試

#[cfg(test)]
fn save(&self) {
    // Don't persist since this isn't production code
}

組合

#[cfg()] 具有使用各種組合處理複雜條件的能力,同時仍然保持可讀。

All

例如,all() 使用邏輯 AND 組合多個條件,確保所有條件的計算值必須爲 true 才能編譯相關代碼。

// 僅在爲32位體系結構的類unix操作系統時編譯
#[cfg(all(unix, target_pointer_width = "32"))]
fn on_32bit_unix() {
    // ...
}

Any

另一方面,any() 使用邏輯 OR 組合多個條件,允許在任何指定條件爲真時編譯代碼。

// 這將在針對Android或iOS時編譯
#[cfg(any(target_os = "android", target_os = "ios"))]
fn on_mobile() {
    // ...
}

Not

此外,not()將條件 (或一組條件) 取反,從而允許在指定條件爲 false 時編譯代碼。這是與前一個函數相反的函數:

// 當不是Android或iOS時將編譯
#[cfg(not(any(target_os = "android", target_os = "ios")))]
fn not_on_mobile() {
    // ...
}

表達式

#[cfg()] 的一個值得注意的應用是編譯時覆蓋函數。通過定義具有不同條件的函數的多個實現,以適應不同的目標平臺或編譯配置。這種靈活性確保只編譯和執行相關的代碼路徑,從而生成更高效的二進制文件。

// 確定Windows的緩存目錄
#[cfg(target_os = "windows")]
const fn default_cache_dir() -> &'static str {
    "C:/cache"
}

// 確定Linux的緩存目錄
#[cfg(target_os = "linux")]
const fn default_cache_dir() -> &'static str {
    "/mnt/c/cache"
}

然後在其他地方這樣調用

generate_cache_items_from(default_cache_dir());

Rust 確保任何未使用的變量或不支持的函數調用都被檢測和適當處理。例如,如果一個函數只在 Linux 目標上運行時可用,那麼調用該函數也必須被標記爲條件以匹配。但是如果有處理其他目標的函數,那麼在編譯時將知道調用哪個函數。

爲了繼續我們前面的例子,嘗試在 Windows 或 Linux 上編譯我們的程序將運行良好。如果我們想讓程序在 MacOS 上編譯,我們需要顯式地聲明語句以支持編譯的目標,如下所示:

#[cfg(any(target_os = "windows", target_os = "linux"))]
generate_cache_items_from(default_cache_dir());

#[cfg(target_os = "macos")]
println!("Caching is not supported on MacOS");

此屬性不限於特定的代碼構造。它可以應用於語句、函數、塊、導入語句等。這種多功能性允許開發人員根據他們的特定需求,在不同粒度級別上選擇性地包含或排除代碼。

Modules

這可能是你以前見過的!

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn insert_and_get() {
        // ...
    }
}

代碼塊

#[cfg(not(vendor_testing))]
{
    trace!("Starting payment processing");
    if let Ok(details) = process_payment(&transaction) {
        db.store(&details);
    }
    trace!("Ending payment processing");
}

#[cfg(vendor_testing)]
{
    debug!("Starting mock payment processing");
    if let Ok(details) = mock_process_payment(&transaction) {
        debug!("Payment details"&details);
    }
    debug!("Ending mock payment processing");
}

枚舉變量

#[non_exhaustive]
pub enum Tab {
    Dashboard,
    Settings,
    #[cfg(feature = "admin")]
    Admin,
}

向量中的元素

impl Tab {
    pub fn all() -> Vec<Self> {
        vec![
            Self::Dashboard,
            Self::Settings,
            #[cfg(feature = "admin")]
            Self::Admin,
        ]
    }
}

結構體

#[cfg(debug_assertions)]
#[derive(Default)]
pub struct DevBuildState {
  dev_id: Mutex<Option<String>>,
}

導入語句

#[cfg(debug_assertions)]
use crate::state::DevBuildState;

Cargo

[target."cfg(not(target_os = "macos"))".dependencies]
some-crate = "0.53.1"

[target."cfg(target_os = "macos")".dependencies]
some-crate-macos-patch = { git = "https://github.com/some-org/some-crate"branch = "macos-patch" }

相關特性

宏操作

除了 #[cfg()] 屬性,Rust 還提供了一個名爲 cfg!() 的宏版本。該宏可用於根據編譯時條件執行內聯 if 語句。

該宏不會有條件地包含 / 排除代碼塊。在編譯時仍然會檢查和替換條件,但實際上不會刪除任何代碼。

window.set_title(if cfg!(feature = "admin") && user.is_admin {
    "Admin User Settings"
} else {
    "User Settings"
});

屬性擴展

這個 #[cfg_attr()] 屬性允許開發人員根據謂詞有條件地包含其他屬性,如下所示:

#[cfg_attr(feature = "magic", sparkles, crackles)]
fn bewitched() {}

// 當啓用' magic '特性標誌時,上述內容將擴展爲
#[sparkles]
#[crackles]
fn bewitched() {}

習慣用法

如果在運行程序時提供了額外的 --cfg 選項:

rustc --cfg "foobar" main.rs

或者通過在構建腳本中指示 Cargo:

println!("cargo:rustc-cfg=foobar");

然後,你可以通過以下方式訪問它:

#[cfg(foobar)]

總結

Rust 的 #[cfg()] 屬性是一個強大的工具,可以在編譯時根據指定的條件有條件地編譯代碼。它可以對代碼的哪些部分包含或排除進行細粒度控制,從而產生更高效和優化的程序!通過混合使用條件和組合,開發人員可以根據不同的平臺、特性和編譯配置定製他們的代碼庫。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/8EubP1HFQY7O_zkk9e3Nkg