How do you use parent module imports in Rust?
I realize this is a very old post and probably wasn't using 2018. However, this can still be really tricky and I wanted to help those out that were looking.
Because Pictures are worth a thousand words I made this simple for code splitting.
Then as you probably guessed they all have an empty pub fn some_function().
We can further expand on this via the changes to main
The additional changes to nested_mod
Let's now go back and answer the question: We added blah1 and blah2 to the mod_1 We added a utils with another mod logging inside it that calls some fn's. Our mod_1/mod.rs now contains:
pub mod blah.rs
pub mod blah2.rs
We created a utils/mod.rs used in main containing:
pub mod logging
Then a directory called logging/with another mod.rs where we can put fns in logging to import.
Source also here https://github.com/DavidWhit/Rust_Modules
Also Check Chapters 7 for libs example and 14.3 that further expands splitting with workspaces in the Rust Book. Good Luck!
I'm assuming you want to declare utils
and utils::logging
at the top level, and just wish to call functions from them inside module1::blah::blah2
. The declaration of a module is done with mod
, which inserts it into the AST and defines its canonical foo::bar::baz
-style path, and normal interactions with a module (away from the declaration) are done with use
.
// main.rs
mod utils {
pub mod logging { // could be placed in utils/logging.rs
pub fn trace(msg: &str) {
println!(": {}\n", msg);
}
}
}
mod module1 {
pub mod blah { // in module1/blah.rs
mod blah2 { // in module1/blah2.rs
// *** this line is the key, to bring utils into scope ***
use crate::utils;
pub fn doit() {
utils::logging::trace("Blah2 invoked");
}
}
pub fn doit() {
blah2::doit();
}
}
}
fn main() {
utils::logging::trace("Logging works");
module1::blah::doit();
}
The only change I made was the use crate::utils;
line in blah2
(in Rust 2015 you could also use use utils
or use ::utils
). Also see the second half of this answer for more details on how use
works. The relevant section of The Rust Programming Language is a reasonable reference too, in particular these two subsections:
- Separating Modules into Different Files
- Bringing Paths into Scope with the use Keyword
Also, notice that I write it all inline, placing the contents of foo/bar.rs
in mod foo { mod bar { <contents> } }
directly, changing this to mod foo { mod bar; }
with the relevant file available should be identical.
(By the way, println(": {}\n", msg)
prints two new lines; println!
includes one already (the ln
is "line"), either print!(": {}\n", msg)
or println!(": {}", msg)
print only one.)
It's not idiomatic to get the exact structure you want, you have to make one change to the location of blah2.rs:
src
├── main.rs
├── module1
│ ├── blah
│ │ └── blah2.rs
│ └── blah.rs
└── utils
└── logging.rs
main.rs
mod utils {
pub mod logging;
}
mod module1 {
pub mod blah;
}
fn main() {
utils::logging::trace("Logging works");
module1::blah::doit();
}
utils/logging.rs
pub fn trace(msg: &str) {
println!(": {}\n", msg);
}
module1/blah.rs
mod blah2;
pub fn doit() {
blah2::doit();
}
module1/blah/blah2.rs (the only file that requires any changes)
// this is the only change
// Rust 2015
// use utils;
// Rust 2018
use crate::utils;
pub fn doit() {
utils::logging::trace("Blah2 invoked");
}
I'm going to answer this question too, for anyone else who finds this and is (like me) totally confused by the difficult-to-comprehend answers.
It boils down to two things I feel are poorly explained in the tutorial:
The
mod blah;
syntax imports a file for the compiler. You must use this on all the files you want to compile.As well as shared libraries, any local module that is defined can be imported into the current scope using
use blah::blah;
.
A typical example would be:
src/main.rs
src/one/one.rs
src/two/two.rs
In this case, you can have code in one.rs
from two.rs
by using use
:
use two::two; // <-- Imports two::two into the local scope as 'two::'
pub fn bar() {
println!("one");
two::foo();
}
However, main.rs
will have to be something like:
use one::one::bar; // <-- Use one::one::bar
mod one { pub mod one; } // <-- Awkwardly import one.rs as a file to compile.
// Notice how we have to awkwardly import two/two.rs even though we don't
// actually use it in this file; if we don't, then the compiler will never
// load it, and one/one.rs will be unable to resolve two::two.
mod two { pub mod two; }
fn main() {
bar();
}
Notice that you can use the blah/mod.rs
file to somewhat alleviate the awkwardness, by placing a file like one/mod.rs
, because mod x;
attempts x.rs
and x/mod.rs
as loads.
// one/mod.rs
pub mod one.rs
You can reduce the awkward file imports at the top of main.rs to:
use one::one::bar;
mod one; // <-- Loads one/mod.rs, which loads one/one.rs.
mod two; // <-- This is still awkward since we don't two, but unavoidable.
fn main() {
bar();
}
There's an example project doing this on Github.
It's worth noting that modules are independent of the files the code blocks are contained in; although it would appear the only way to load a file blah.rs
is to create a module called blah
, you can use the #[path]
to get around this, if you need to for some reason. Unfortunately, it doesn't appear to support wildcards, aggregating functions from multiple files into a top-level module is rather tedious.