Conditional Modules
Conditional modules use #[cfg(...)] on mod declarations and pub use re-exports to organize platform-specific or feature-gated implementations into clean, self-contained files without cluttering individual functions with fine-grained #[cfg] blocks.
Pattern 1: Platform-Dispatched Private Backend Modules (mod imp)
The standard library (std::sys) and cross-platform crates (tokio, mio, rustix) isolate OS differences by placing each platform's implementation in its own module (unix.rs, windows.rs) that exposes an identical internal interface, and then re-exporting the active module under a single private name (use self::imp::*;):
Cross-Platform Module Architecture:
src/fs_watcher.rs (Public Cross-Platform Façade)
+-- #[cfg(unix)] mod imp { /* kqueue / inotify implementation */ }
+-- #[cfg(windows)] mod imp { /* ReadDirectoryChangesW implementation */ }
|
+-- pub struct Watcher(imp::OsWatcher); <-- Unified public type!
#[cfg(unix)]
mod imp {
pub struct NativePoller {
fd: i32,
}
impl NativePoller {
pub fn new() -> Self {
Self { fd: 4 }
}
pub fn backend_name(&self) -> &'static str {
"unix-poll (fd-based)"
}
pub fn raw_id(&self) -> usize {
self.fd as usize
}
}
}
#[cfg(windows)]
mod imp {
pub struct NativePoller {
iocp_handle: usize,
}
impl NativePoller {
pub fn new() -> Self {
Self { iocp_handle: 0x100 }
}
pub fn backend_name(&self) -> &'static str {
"windows-iocp"
}
pub fn raw_id(&self) -> usize {
self.iocp_handle
}
}
}
// Public cross-platform wrapper delegates to `imp::NativePoller` with ZERO `#[cfg]`
// annotations inside the public methods:
pub struct Poller {
inner: imp::NativePoller,
}
impl Poller {
pub fn new() -> Self {
Self {
inner: imp::NativePoller::new(),
}
}
pub fn info(&self) -> String {
format!(
"backend = {}, handle_id = {}",
self.inner.backend_name(),
self.inner.raw_id()
)
}
}
fn main() {
let poller = Poller::new();
println!("{}", poller.info());
}
backend = unix-poll (fd-based), handle_id = 4
Pattern 2: Test-Only Modules (#[cfg(test)] mod tests)
Placing #[cfg(test)] above mod tests { ... } ensures that unit tests and test-only helper functions are compiled only when running cargo test (rustc --test), and are completely excluded from normal cargo build and --release artifacts.
Pattern 3: Custom Module Source Paths with #[path = "..."]
When mapping multiple conditional modules to files inside a platform subdirectory (src/sys/unix.rs vs. src/sys/windows.rs), the #[path = "..."] attribute overrides the default file lookup path for mod sys;:
// #[cfg_attr(unix, path = "sys/unix.rs")]
// #[cfg_attr(windows, path = "sys/windows.rs")]
// mod sys;