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