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.

enter image description here

enter image description here

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 enter image description here

enter image description here

The additional changes to nested_mod enter image description here

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.

enter image description here

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.

Tags:

Rust