# Rust 条件编译
下面是一个简单的 Rust 条件编译的例子,它展示了如何使用 cfg
属性来选择在哪个平台上编译代码。
#[cfg(target_os = "windows")] | |
fn main() { | |
println!("This is Windows."); | |
} | |
#[cfg(target_os = "linux")] | |
fn main() { | |
println!("This is Linux."); | |
} | |
#[cfg(not(any(target_os = "windows", target_os = "linux")))] | |
fn main() { | |
println!("This is neither Windows nor Linux."); | |
} |
下面使用了 any
、 not
、 all
三个关键字,分别表示 “任意一个特性被启用”、“没有任何一个特性被启用”、“所有特性都被启用” 等条件。这个例子可以根据不同的特性组合打印出不同的信息,可以在编译时根据需要指定不同的特性来编译程序。
fn main() { | |
#[cfg(any(feature = "foo", feature = "bar"))] | |
println!("Foo or Bar feature is enabled"); | |
#[cfg(not(any(feature = "foo", feature = "bar")))] | |
println!("Neither Foo nor Bar feature is enabled"); | |
#[cfg(all(feature = "foo", not(feature = "bar")))] | |
println!("Only Foo feature is enabled"); | |
#[cfg(all(not(feature = "foo"), feature = "bar"))] | |
println!("Only Bar feature is enabled"); | |
} |
这个例子展示了如何使用 #[cfg]
属性来针对不同的平台和条件进行条件编译。这个程序输出当前系统的操作系统名称,架构,指针位数和调试模式状态。 #[cfg]
属性还支持逻辑运算符,使得可以更灵活地控制编译时执行的代码块。
fn main() { | |
#[cfg(target_os = "windows")] | |
println!("This is Windows."); | |
#[cfg(target_os = "macos")] | |
println!("This is macOS."); | |
#[cfg(target_os = "linux")] | |
println!("This is Linux."); | |
#[cfg(all(target_arch = "x86_64", target_os = "linux"))] | |
println!("This is a 64-bit Linux system."); | |
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))] | |
println!("This is an ARM system."); | |
#[cfg(target_pointer_width = "32")] | |
println!("This is a 32-bit system."); | |
#[cfg(target_pointer_width = "64")] | |
println!("This is a 64-bit system."); | |
#[cfg(debug_assertions)] | |
println!("Debug mode is on."); | |
#[cfg(not(debug_assertions))] | |
println!("Debug mode is off."); | |
} |
下面这个例子根据不同的平台选择不同的实现,其中:
#[cfg(any(unix, windows))]
表示只有在 Unix 或者 Windows 平台才编译这个模块;#[cfg_attr(all(unix, not(target_os = "macos")), path = "platforms/linux.rs")]
表示如果是 Linux 平台则使用platforms/linux.rs
文件,否则跳过;#[cfg_attr(all(unix, target_os = "macos"), path = "platforms/macos.rs")]
表示如果是 macOS 平台则使用platforms/macos.rs
文件,否则跳过;#[cfg(windows)]
表示如果是 Windows 平台则使用platforms/windows.rs
文件;mod platform;
表示引入platform
模块,实际上会根据条件编译的结果选择不同的实现文件。
这个例子比之前的例子更加复杂,它涉及到了多个条件的判断,以及根据条件选择不同的文件进行编译。
#[cfg(any(unix, windows))] | |
#[cfg_attr( | |
all(unix, not(target_os = "macos")), | |
path = "platforms/linux.rs" | |
)] | |
#[cfg_attr( | |
all(unix, target_os = "macos"), | |
path = "platforms/macos.rs" | |
)] | |
#[cfg(windows)] | |
#[path = "platforms/windows.rs"] | |
mod platform; |
下面是一个使用 #[cfg_attr]
属性的例子:
在这个例子中, #[cfg_attr(debug_assertions, allow(dead_code))]
指定了在 debug 模式下允许未使用的函数。另外,根据不同的目标架构,使用了不同的代码。如果目标架构是 x86_64,则 let x = 42
语句会被编译,如果是 aarch64,则 let x = 0
语句会被编译。在实际编译时,根据条件会为代码添加不同的属性。
#[cfg_attr(debug_assertions, allow(dead_code))] | |
fn foo() { | |
#[cfg(target_arch = "x86_64")] | |
let x = 42; | |
#[cfg(target_arch = "aarch64")] | |
let x = 0; | |
println!("x = {}", x); | |
} |
在下面的例子中, foo
函数只有在 foo
特性启用时才会编译。 bar_or_baz
函数只有在 bar
或 baz
两个特性之一启用时才会编译,并使用了 cfg!(...)
宏来检查当前是否启用了 bar
特性。 qux_without_baz
函数只有在 qux
特性启用且 baz
特性未启用时才会编译。最后, no_features_enabled
函数只有在没有任何特性启用时才会编译。
你可以根据需要调整特性的名称和条件来满足你的要求。
#[cfg(feature = "foo")] | |
fn foo() { | |
println!("foo feature enabled"); | |
} | |
#[cfg(any(feature = "bar", feature = "baz"))] | |
fn bar_or_baz() { | |
if cfg!(feature = "bar") { | |
println!("bar feature enabled"); | |
} else { | |
println!("baz feature enabled"); | |
} | |
} | |
#[cfg(all(feature = "qux", not(feature = "baz")))] | |
fn qux_without_baz() { | |
println!("qux feature enabled, but baz is not enabled"); | |
} | |
#[cfg(not(any(feature = "foo", feature = "bar", feature = "baz", feature = "qux")))] | |
fn no_features_enabled() { | |
println!("No features enabled"); | |
} | |
fn main() { | |
foo(); | |
bar_or_baz(); | |
qux_without_baz(); | |
no_features_enabled(); | |
} |
还可以通过结合其他宏实现更复杂的条件编译。以下是一个稍微复杂一些的例子:
macro_rules! debug_println { | |
() => {}; | |
($($args:expr),*) => { | |
#[cfg(feature = "debug_print")] | |
{ | |
println!("[DEBUG] {}", format_args!($($args),*)); | |
} | |
}; | |
} | |
macro_rules! error_println { | |
() => {}; | |
($($args:expr),*) => { | |
#[cfg(feature = "error_print")] | |
{ | |
println!("[ERROR] {}", format_args!($($args),*)); | |
} | |
}; | |
} | |
macro_rules! info_println { | |
() => {}; | |
($($args:expr),*) => { | |
#[cfg(feature = "info_print")] | |
{ | |
println!("[INFO] {}", format_args!($($args),*)); | |
} | |
}; | |
} | |
fn main() { | |
let x = 10; | |
debug_println!("The value of x is {}", x); | |
error_println!("An error occurred!"); | |
info_println!("This is an information message."); | |
} | |
/* 这个例子使用了三个宏来打印不同类型的日志,每个宏都带有条件编译,仅在启用相应特性时才会执行打印操作。可以在编译时使用不同的特性来控制打印哪些日志,例如:*/ | |
$ cargo run --features debug_print # 打印调试信息 | |
[DEBUG] The value of x is 10 | |
$ cargo run --features error_print # 打印错误信息 | |
[ERROR] An error occurred! | |
$ cargo run --features info_print # 打印信息日志 | |
[INFO] This is an information message. | |
// 这种方法可以帮助开发人员在不同的情况下,对代码进行不同程度的详细度调试。同时,还可以根据项目需要轻松地开启或关闭日志输出。 |
以下是一个结合条件编译和声明宏的复杂例子:
#[macro_use] | |
#[cfg(feature = "my_feature")] | |
mod my_module { | |
#[cfg(target_arch = "x86")] | |
macro_rules! my_macro { | |
() => { | |
println!("my_macro for x86"); | |
}; | |
} | |
#[cfg(target_arch = "x86_64")] | |
macro_rules! my_macro { | |
() => { | |
println!("my_macro for x86_64"); | |
}; | |
} | |
pub fn my_function() { | |
my_macro!(); | |
} | |
} |
# Rust 的条件编译,可以总结为以下几个部分:
1.cfg 属性 (attribute)
句法
CfgAttrAttribute :
cfg ( ConfigurationPredicate )
#[attribute]
可以应用于表达式、函数、模块、变量等各种语法结构,用于控制编译器的行为。可以使用 cfg
属性指定条件编译。
例如:
#[cfg(feature = "my_feature")] | |
fn my_func() { | |
// do something | |
} |
这段代码中, #[cfg(feature = "my_feature")]
属性表明 my_func()
函数只有在启用了名为 my_feature
的特性时才会被编译。
2.cfg_if
这段代码中, #[cfg(target_os = "linux")]
属性表明 my_func()
函数只有在编译目标平台为 Linux 时才会被编译。 cfg_if!
宏则根据不同的条件编译选项选择不同的代码块。例如:
#[cfg(target_os = "linux")] | |
fn my_func() { | |
// do something | |
} | |
// ... | |
fn main() { | |
cfg_if::cfg_if! { | |
if #[cfg(target_os = "linux")] { | |
// linux-specific code | |
} else if #[cfg(target_os = "macos")] { | |
// macos-specific code | |
} else { | |
// generic code | |
} | |
} | |
} |
3.cfg_matches! 宏
cfg_matches!
宏用于检查一个属性是否与指定的条件匹配。可以使用它来自定义条件编译选项。
例如:
fn my_func() { | |
cfg_if::cfg_if! { | |
if #[cfg_matches!(feature = "my_feature")] { | |
// feature-specific code | |
} else { | |
// generic code | |
} | |
} | |
} |
这段代码中, #[cfg_matches!(feature = "my_feature")]
属性表明 my_func()
函数只有在启用了名为 my_feature
的特性时才会执行特定的代码块。
4.if/else 块
Rust 的条件编译也可以使用 if/else 块来实现。
例如:
fn my_func() { | |
#[cfg(feature = "my_feature")] | |
{ | |
// feature-specific code | |
} | |
#[cfg(not(feature = "my_feature"))] | |
{ | |
// generic code | |
} | |
} |
这段代码中, #[cfg(feature = "my_feature")]
和 #[cfg(not(feature = "my_feature"))]
分别表示在启用和禁用 my_feature
特性时执行不同的代码块。
# Rust 条件编译上下文无关文法
Rust 的条件编译系统提供了一种强大的方法,使得开发者能够根据不同的条件来编写和编译代码。条件编译的语法类似于 C++ 和 C#,使用 #[cfg(condition)]
属性来指定编译条件。Rust 的条件编译语法可以总结为以下上下文无关文法:
cfg 属性的上下文无关文法:
其中,meta_item表示一个元数据项,可以是一个标识符、一个带值的标识符、一个带参数的标识符或者一个代码块。meta_item_list表示一个元数据项列表,包含若干个用逗号分隔的元数据项。meta表示一个元数据,可以是一个单独的元数据项,或者由多个元数据项组成的列表。attribute表示一个属性,包含一个由方括号括起来的元数据列表。 | |
meta_item ::= | |
IDENTIFIER | |
| IDENTIFIER '=' LITERAL | |
| IDENTIFIER '(' meta_item_list? ')' | |
| IDENTIFIER '{' meta_item* '}' | |
meta_item_list ::= | |
meta_item (',' meta_item)* | |
meta ::= | |
meta_item | |
| meta_item ',' meta | |
attribute ::= | |
'#' '[' meta ']' |
# cfg_if 宏
#[macro_export] | |
macro_rules! my_cfg_if { | |
( | |
$(if #[$attr:meta] $type_alias:ident = $ty:ty;)+ | |
$(else if #[$attr_else:meta] $type_alias_else:ident = $ty_else:ty;)+ | |
$(else $type_alias_default:ident = $ty_default:ty;)* | |
) => { | |
$( | |
#[cfg($attr)] | |
pub type $type_alias = $ty; | |
)+ | |
$( | |
#[cfg(not($attr))][cfg($attr_else)] | |
pub type $type_alias_else = $ty_else; | |
)* | |
$( | |
#[cfg(not(any($($attr),+)))] | |
pub type $type_alias_default = $ty_default; | |
)* | |
}; | |
} |
使用:
my_cfg_if!( | |
if #[cfg(feature = "a")] MyType = i32; | |
else if #[cfg(feature = "b")] MyType = f32; | |
else MyType = u32; | |
); |