R
RUSTY YELLOW PAGES

#[path = "..."]Attribute


Explanation

#[path = "..."] overrides where a mod name; declaration looks for its source file on disk, replacing the default name.rs / name/mod.rs convention with an explicit path, resolved relative to the directory of the file containing the attribute — #[path = "impl/windows.rs"] mod platform; loads platform's contents from impl/windows.rs instead of the default platform.rs. The attribute can also sit on a mod name { ... } block that itself contains further mod other; declarations, changing the base directory those nested declarations resolve from — but in practice it's almost always attached to a mod name; file-loading declaration, which is the form nearly every real use targets.

This is a rare attribute: most crates never need it, because letting the on-disk layout mirror the module tree — the default convention — is simpler for any reader to navigate, and is what every other Rust codebase already expects. It's reached for mainly in two situations: a project whose directory layout can't or doesn't want to mirror the module tree (most commonly, picking a different implementation file per target, #[path = "sys/windows.rs"] mod sys; versus #[path = "sys/unix.rs"] mod sys;, each behind its own #[cfg(...)]), or loading a file generated at build time into an ordinary module path. See Modules for how the default file-lookup convention works; this page covers only the override.

Usage examples

Selecting a platform-specific file with #[cfg] and #[path]

#[cfg(target_os = "windows")]
#[path = "backend_windows.rs"]  // <- overrides the default `backend.rs` lookup
mod backend;

#[cfg(not(target_os = "windows"))]
#[path = "backend_unix.rs"]     // <- same module name, a different file on other platforms
mod backend;

Designing a public API

A cross-platform crate exposes one backend module to the rest of its code, but the actual implementation file differs per target OS — #[path] points mod backend; at whichever file matches the platform being compiled for, so callers see one stable module regardless of platform.

// src/lib.rs
#[cfg(windows)]
#[path = "backend/windows.rs"] // <- overrides `backend.rs`/`backend/mod.rs`; only compiled on Windows
mod backend;

#[cfg(not(windows))]
#[path = "backend/unix.rs"]    // <- same module name, routed to a different file elsewhere
mod backend;

pub use backend::current_user_home; // one stable public path regardless of which file was loaded

// src/backend/windows.rs
pub fn current_user_home() -> String {
    std::env::var("USERPROFILE").unwrap_or_default()
}

// src/backend/unix.rs
pub fn current_user_home() -> String {
    std::env::var("HOME").unwrap_or_default()
}

Pairing #[path] with #[cfg(...)] keeps one logical backend module name across platforms while routing to a genuinely different file per target, rather than #[cfg]-gating code inline inside a single shared file — the exact override behavior is documented in the Rust Reference's module path attribute section.

Explanation

Embedded support: Full

There isn't a mechanically different story here — #[path] is a purely compile-time source-file resolution attribute, and it does exactly the same thing in a #![no_std] crate that it does anywhere else. What's worth saying is simply where embedded Rust ends up using the same #[cfg] + #[path] pairing already shown in the classic section: chip- family HAL crates that support many closely related microcontroller variants (an stm32f4xx-hal supporting the STM32F401, F405, F411, and dozens of other part numbers, or embassy's per-chip support) commonly select one Cargo feature per variant, then use #[path] to route a shared module name — a peripheral memory map, a pin-to-alternate-function table generated from that specific chip's SVD file — to whichever file matches the feature that's actually enabled, without needing a separate module tree, or a separate crate, per supported chip.

Usage examples

Selecting a chip-specific peripheral map by Cargo feature

// src/lib.rs
#[cfg(feature = "stm32f405")]
#[path = "chip/stm32f405.rs"] // <- routes the shared `chip` module to this variant's register map
mod chip;

#[cfg(feature = "stm32f411")]
#[path = "chip/stm32f411.rs"] // <- same module name, a different generated file for this variant
mod chip;

pub use chip::GPIOA_BASE; // one stable path regardless of which chip feature is enabled

// src/chip/stm32f405.rs
pub const GPIOA_BASE: u32 = 0x4002_0000;

// src/chip/stm32f411.rs
pub const GPIOA_BASE: u32 = 0x4002_0000; // may legitimately differ per part in a real HAL

This is the same override behavior as the classic Windows/Unix example, just keyed on a chip-variant Cargo feature instead of a target OS — #[path] itself carries no embedded-specific behavior; only the axis being switched on changes.