How *exactly* does rust look up modules? - rust

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:
Rust looks at the name, parsing :: like subdir::subdir::name
Rust looks to see if there is a file name.rs in the same directory and name/mod.rs
There is not allowed to be both a name.rs and a name/mod.rs.
Then, Rust...???

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 a; 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.
crate:: just always corresponds to the root 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.

Related

Why do I need both mod and use to bring a module to the scope?

Why do I need to write mod and use when I want to bring a module to the scope?
mod from_other_file;
use from_other_file::sub_module;
fn main() {
sub_module::do_something();
}
If I do this it gives me error because the module isn't imported inside the current file:
use from_other_file::sub_module;
fn main() {
sub_module::do_something();
}
Error Message:
error[E0432]: unresolved import `from_other_file`
--> src/main.rs:1:5
|
1 | use from_other_file::sub_module;
| ^^^^^^^^^^^^^^^ use of undeclared type or module `from_other_file`
use and mod are doing two very different things.
mod declares a module. It has two forms:
mod foo {
// Everything inside here is inside the module foo
}
// Look for a file 'bar.rs' in the current directory or
// if that does not exist, a file bar/mod.rs. The module
// bar contains the items defined in that file.
mod bar;
use, on the other hand, brings items into the current scope. It does not play a part in defining which files should be considered part of which module namespaces, rather it just brings items that the compiler already knows about (such as locally declared modules and/or external dependencies from the Cargo.toml file) into the scope of the current file.
Modules in Rust should be declared explicitly with mod declaration.
Without mod from_other_file; which introduces the name from_other_file into the current scope Rust knows nothing about it. The name refers to module item.

How to reference a constant in a test in the same lib.rs file in Rust?

I have a constants defined in a lib.rs as follows:
const GREEN: LedColor = LedColor(0, 255, 0);
In the same lib.rs file I also have tests trying to use GREEN as follows:
#[cfg(test)]
mod tests {
use {OFF, YELLOW, RED, GREEN};
#[test]
fn some_test() {//...}
But running cargo test gives an error such as:
no GREEN in path
How do I reference the constant GREEN in a test that's in the same file?
You need to use the super keyword, to reference the parent module.
The module tests is actually crate::tests, which means GREEN the way you've written it there is really crate::tests::GREEN. That doesn't exist, as GREEN is defined in the parent module. So you need:
#[cfg(test)]
mod tests {
use super::{OFF, YELLOW, RED, GREEN};
}
These are considered private so a normal use crate::{names} wouldn't work.
You can use use super::* (* makes them all available, as a shorthand) which brings in private names from the parent module. (though this isn't documented from what I could find)
If you don't mind making them public, you can add pub and then use use crate::{names}.

mod.rs and nested modules in Rust

I want to specify a generic trait Engine and provide two different implementations: Random and Dummy. I want to use following file structure:
src-
|-engine
|-mod.rs //contains Engine trait code
|-random.rs // contains first engine implementation
|-dummy.rs // contains second engine implementation
I have something like this in lib.rs:
pub mod engine {
// random moves engine
pub mod random;
pub mod dummy;
}
When I try to add use engine::Engine anywhere in my other modules, it can't be found:
error[E0432]: unresolved import `engine::Engine`
When you write curly braces in a mod item, that tells the compiler that the module's contents are within these braces, not in an external file. Thus, the compiler doesn't even look at engine/mod.rs. When you write a semicolon instead, the compiler will look for an external file.
What you need to do instead is put these lines in engine/mod.rs:
pub mod random;
pub mod dummy;
And in lib.rs, write:
pub mod engine;

How many lines are covered by the Rust conditional compilation attribute?

I'm trying to use a conditional compilation statement. Beyond defining a function that should only exist in a debug build, I want to define a set of variables/constants/types that only exist in the debug build.
#[cfg(debug)]
pub type A = B;
pub type B = W;
#[cfg(other_option)]
pub type A = Z;
pub type B = I;
let test = 23i32;
How many lines are actually "covered" by the conditional compile attribute in this case? Is it only one (what I would expect in this context)? Are there ways to ensure that a whole block of code (including variables, types and two functions) is covered by the condition?
You can use a module to group together everything that should exist for debug/release only, like this:
#[cfg(debug)]
mod example {
pub type A = i32;
pub type B = i64;
}
#[cfg(not(debug))]
mod example {
pub type A = u32;
pub type B = u64;
}
fn main() {
let x: example::A = example::A::max_value();
println!("{}", x);
}
Playground link (note that this will always print the not(debug) value because the playground doesn't define the debug feature, even in debug mode).
If debug is defined, this will print 2147483647 (the maximum value of an i32), otherwise it will print 4294967295 (the maximum value of a u32). Keep in mind that both modules must have definitions for each item, otherwise you'll hit a compile-time error.
If you've not read about Attributes, it might be a good idea to do so; make sure you know the difference between inner attributes (#![attribute]) and outer attributes (#[attribute]).
An #[attribute] only applies to the next item. Please see the Rust book.
Edit: I don't think it is currently possible to spread an attribute over an arbitrary number of declarations.
Additional, in-depth information on attributes and their application can be found at Rust reference.

How are mod & use supposed to work for traits in rust?

Consider the following contrived situation:
mod imported {
pub trait Imported {
fn hello(&self, x:int) -> int;
}
}
struct Hi;
impl imported::Imported for Hi {
fn hello(&self, x:int) -> int {
return x;
}
}
#[test]
fn test_thing() {
let value = Hi;
println!("{:?}", value.hello(10));
}
This won't compile, because the trait Imported is not in scope, so the method hello() cannot be invoked:
imports.rs:20:18: 20:33 error: type `imports::Hi` does not implement any method in scope named `hello`
imports.rs:20 println!("{:?}", value.hello(10));
^~~~~~~~~~~~~~~
If we put Imported in the current scope (ie. get rid of mod imported) this works fine, but like this, it does not.
Normally for this purpose you would use 'use' to bring the 'Imported' symbol into the local scope:
use imported::Imported;
However, in this case you cannot, because the symbol 'imported' does not yet exist at the beginning of the file:
imports.rs:2:5: 2:13 error: unresolved import. maybe a missing `extern crate imported`?
imports.rs:2 use imported::Imported;
^~~~~~~~
imports.rs:2:5: 2:23 error: failed to resolve import `imported::Imported`
imports.rs:2 use imported::Imported;
^~~~~~~~~~~~~~~~~~
And you cannot do it after the mod imported call, because:
imports.rs:8:1: 8:24 error: `use` and `extern crate` declarations must precede items
imports.rs:8 use imported::Imported;
^~~~~~~~~~~~~~~~~~~~~~~
To be fair this only occurs when you are implementing a trait and then trying to use that impl in the safe file, but it seems like this would actually be quite a common use case for testing.
You can work around it by structuring your code slightly differently and importing modules from some common parent, but this seems like an inherent limitation of the current system.
Am I missing something?
I think you have indeed missed something. Adding use imported::Imported; at the top of the file does work—I presume that this is not precisely what you tried to compile.
use statements, although written before the mod statements, are resolved after the mod, so there is no source order problem.
I presume that your error is in fact that your file imports.rs was not the crate root. Remember that a use statement takes an absolute path if you don't use super or self in it. To make it work in any place, you would need to write use self::imported::Imported;; assuming you were compiling a lib.rs containing mod imports;, you would have needed to use imports::imported::Imported, the absolute path to the trait.
(BTW, you can replace return x; at the end of the function with just x. Remember Rust's block value doctrine, whereby the last expression in a block is the value of the block.)

Resources