Attributes and Conditional Compilation

Platform-Specific Code

Rust provides built-in #[cfg(...)] target predicates and platform-specific standard library extension modules (std::os::unix, std::os::windows, std::os::fd) to write platform-specific code while presenting a unified cross-platform API to callers.

Operating System and Environment #[cfg] Predicates

Predicate Key Common Values Meaning
target_family (or shorthand) unix, windows, wasm Broad OS family (#[cfg(unix)] is shorthand for #[cfg(target_family = "unix")], matching Linux, macOS, FreeBSD, iOS, Android)
target_os "linux", "macos", "windows", "freebsd", "ios", "android", "none" Specific operating system kernel ("none" for bare-metal targets)
target_env "gnu", "musl", "msvc", "sgx", "" C runtime / ABI environment (distinguishes x86_64-unknown-linux-gnu from x86_64-unknown-linux-musl)
target_vendor "apple", "pc", "unknown", "fortanix" Hardware / OS vendor
Relationship Between `target_family` and `target_os`:

  #[cfg(unix)]  (`target_family = "unix"`)
    +-- target_os = "linux"   (target_env = "gnu" | "musl")
    +-- target_os = "macos"   (target_vendor = "apple")
    +-- target_os = "freebsd"
    +-- target_os = "android" / "ios"

  #[cfg(windows)] (`target_family = "windows"`)
    +-- target_os = "windows" (target_env = "msvc" | "gnu")

Accessing OS-Specific Extension Traits (std::os)

The Rust standard library keeps std::fs::File, std::process::Command, and std::net::TcpStream strictly portable, and exposes OS-specific capabilities—such as raw file descriptors (RawFd), Unix file permissions (mode), inode numbers (ino), or Windows security attributes—through extension traits under std::os::<platform>:

use std::fs::File;

#[cfg(unix)]
fn inspect_os_handle(file: &File) {
    use std::os::fd::AsRawFd;
    use std::os::unix::fs::MetadataExt;

    let fd = file.as_raw_fd();
    let meta = file.metadata().unwrap();
    println!(
        "Unix file -> raw fd = {}, mode = {:o}, ino = {}",
        fd,
        meta.mode() & 0o777,
        meta.ino()
    );
}

#[cfg(windows)]
fn inspect_os_handle(file: &File) {
    use std::os::windows::io::AsRawHandle;
    println!("Windows file -> raw HANDLE = {:?}", file.as_raw_handle());
}

fn main() -> std::io::Result<()> {
    let path = std::env::temp_dir().join("rust_ch42_platform_demo.tmp");
    let file = File::create(&path)?;

    inspect_os_handle(&file);

    drop(file);
    std::fs::remove_file(&path)?;
    Ok(())
}
Unix file -> raw fd = 3, mode = 644, ino = 48192014

Structuring Cross-Platform Abstractions Without Duplicating Call Sites

Never scatter #[cfg(unix)] and #[cfg(windows)] across application business logic. Instead, define a single cross-platform function or struct signature and place the platform-specific implementations inside private helper functions or platform modules so callers remain 100% platform-agnostic.