Test
Test

Reputation: 1730

How *exactly* does rust look up modules?

What is the exact set of rules that rust uses to look up a module from a file?

Every explanation I have found online about modules says, "this is the purpose of modules, here is an example of one, ..." None give the complete, comprehensive, 100% accurate explanation for how rust looks up modules. Even the rust reference doesn't tell you whether both the crate root and the importing file need to declare mod! There is no simple ruleset I can use to tell whether it will work.

I'm looking for something I can follow, like:

  1. Rust looks at the name, parsing :: like subdir::subdir::name
  2. Rust looks to see if there is a file name.rs in the same directory and name/mod.rs
  3. There is not allowed to be both a name.rs and a name/mod.rs.
  4. Then, Rust...???

Upvotes: 2

Views: 912

Answers (1)

PitaJ
PitaJ

Reputation: 15094

This is best explained starting from inline modules. Modules are arranged into a hierarchy from the crate root. Every crate, after some desugaring, looks something like this:

// root

pub mod a {
    pub mod b {
        pub const X: u8 = 1;
    }
}

mod foo {
    
}

Referring to an item in the tree is pretty simple:

  • :: goes "down" a level
  • super:: goes "up" a level
  • crate:: goes to the root level

Examples for referring to X:

  • a::b::X from the crate root
  • crate::a::b::X from anywhere in the crate
  • super::a::b::X from within module foo
  • b::X from within module a

mod foo; is really just syntax sugar for either of the following:

#[path = "foo.rs"]
mod foo;
// or
#[path = "foo/mod.rs"]
mod foo;

Which further desugar to:

mod foo {
    include!("foo.rs");
}
// or
mod foo {
    include!("foo/mod.rs");
}

If foo.rs (or foo/mod.rs) contains a mod bar; then the whole tree would look like:

mod foo {
    mod bar {
        // contents of `bar.rs` (or `foo/bar/mod.rs`)
    }

    // remaining contents of `foo.rs`
}

Please note that the usage of mod.rs, while still supported, is discouraged. Instead, it's recommended to use foo.rs for crate::foo and place any submodules of foo in the foo/ directory.

If both src/foo/mod.rs and src/foo.rs exist, the compiler will throw the following error:

error[E0761]: file for module `foo` found at both "src\foo.rs" and "src\foo\mod.rs"
 --> src\main.rs:1:1
  |
1 | mod foo;
  | ^^^^^^^^
  |
  = help: delete or rename one of them to remove the ambiguity

crate:: just always corresponds to the root module of the crate being compiled at the time. If your crate is sufficiently complex or doesn't follow convention, then certain crate::... item paths can refer to different things in different files. But confusion is easily avoidable by following conventions.

Here's the list of conventions I would use to avoid confusion:

  • avoid #[path] attributes
  • always use src/foo.rs instead of src/foo/mod.rs
  • if you have multiple root modules (for instance a library and binary crate), they should all live directly in src
  • minimize fancy configuration in Cargo.toml, use defaults where possible

Upvotes: 6

Related Questions