人人都会AI编程

24.2 Rust 条件编译:cfg 属性实现平台差异化代码

更新时间:2026-07-11

在 Tauri 开发中,虽然 WebView 抽象掉了多数平台差异,但仍有不少场景需要编写平台特定的 Rust 代码——比如加载不同路径的动态库、处理 Windows 特有的注册表逻辑、设置 macOS 的 Plist 权限等。Rust 的条件编译机制允许你在编译时有选择地包含或排除代码,从而用同一份源码应对不同操作系统,而无需手动维护多个分支。

1. #[cfg] 属性:编译时的开关

最常见的方式是用 #[cfg] 修饰函数、代码块或结构体,根据条件决定是否编译该段代码。条件可以是目标操作系统(target_os)、目标架构(target_arch)、编译特性(feature)等。

示例:不同平台的系统休眠禁用

// 仅在 Windows 上编译
#[cfg(target_os = "windows")]
fn prevent_sleep() {
    // 调用 Windows API 阻止显示器关闭
    windows_sys::Win32::System::Power::SetThreadExecutionState(
        windows_sys::Win32::System::Power::ES_CONTINUOUS | windows_sys::Win32::System::Power::ES_DISPLAY_REQUIRED
    );
}

// 仅在 macOS 上编译
#[cfg(target_os = "macos")]
fn prevent_sleep() {
    // 启动一个隐藏的 caffeinate 进程
    std::process::Command::new("caffeinate")
        .arg("-d")
        .spawn()
        .expect("Unable to start caffeinate");
}

// 其他平台提供空实现
#[cfg(not(any(target_os = "windows", target_os = "macos")))]
fn prevent_sleep() {
    // 不支持,静默忽略
}

使用时直接调用 prevent_sleep(),实际链接的只会是当前平台对应的那个函数。

2. cfg! 宏:运行时检查(本质仍是编译时)

cfg!() 宏返回一个布尔值,可以用于 if 判断,但它在编译时就会求值,因此未匹配的分支会被完全移除,不会产生任何死代码。这对于需要保持同一个函数体但内部逻辑分叉的场景很方便。

if cfg!(target_os = "windows") {
    windows_specific_init();
} else if cfg!(target_os = "macos") {
    macos_speicific_init();
}

编译后仅保留与目标平台匹配的那一行,其余分支会被优化掉。

3. #[cfg_attr]:条件地应用属性

有时你需要根据平台为一个结构体或函数附加不同的属性。例如,Windows 上可能需要导出特定符号,而 Linux 上需要禁用某个 lint。

#[cfg_attr(target_os = "windows", link(name = "user32"))]
extern "C" { fn MessageBoxA(...) -> i32; }

#[cfg_attr(target_os = "linux", allow(dead_code))]
fn linux_only_helper() { }

4. 在 Tauri 命令中区分平台

一个典型的 Tauri 命令可能需要在不同平台执行不同的系统操作。你可以将平台相关逻辑封装在 Rust 模块中,并用 cfg 导入不同的实现。

文件结构:

src/
  commands/
    platform.rs       // 公共接口
    platform_windows.rs
    platform_macos.rs
    platform_linux.rs

platform.rs(条件编译驱动):

#[cfg(target_os = "windows")]
mod platform_windows;
#[cfg(target_os = "windows")]
pub use platform_windows::get_system_fonts;

#[cfg(target_os = "macos")]
mod platform_macos;
#[cfg(target_os = "macos")]
pub use platform_macos::get_system_fonts;

#[cfg(target_os = "linux")]
mod platform_linux;
#[cfg(target_os = "linux")]
pub use platform_linux::get_system_fonts;

然后在 Tauri 命令中调用 get_system_fonts(),无需任何 if 判断。

5. 常用条件键

  • target_os"windows""macos""linux""android""ios"
  • target_arch"x86""x86_64""arm""aarch64"
  • target_env"msvc""gnu""musl",对 Windows 区分 MSVC 与 GNU 工具链有用
  • feature:自定义构建特性,如 #[cfg(feature = "rich_clipboard")]
  • debug_assertionstarget_pointer_width

6. 实践建议

  • 尽量避免大量的 cfg 散落在业务逻辑中,集中到平台抽象层,让大部分代码保持干净。
  • 使用 cargo check --target x86_64-pc-windows-msvc(或 macOS/Linux triple)快速验证条件编译在不同目标下是否通过,无需真实运行。
  • 结合 tauricustom-protocol 或资源文件,可以在 Rust 侧根据平台返回不同的前端配置,但 Rust 条件编译仍是最底层、最可控的方式。

通过合理运用条件编译,你可以用一套代码库覆盖所有平台,同时仍然保留了针对每个平台深度优化的可能性——这正是 Tauri 在维持小巧体积的同时,依然能调用原生能力的关键机制之一。