HiveBrain v1.2.0
Get Started
← Back to all entries
snippetrustModerate

How do you use parent module imports in Rust?

Submitted by: @import:stackoverflow-api··
0
Viewed 0 times
youhowmoduleimportsparentuserust

Problem

If you have a directory structure like this:

src/main.rs
src/module1/blah.rs
src/module1/blah2.rs
src/utils/logging.rs


How do you use functions from other files?

From the Rust tutorial, it sounds like I should be able to do this:

main.rs

mod utils { pub mod logging; }
mod module1 { pub mod blah; }

fn main() {
    utils::logging::trace("Logging works");
    module1::blah::doit();
}


logging.rs

pub fn trace(msg: &str) {
    println!(": {}\n", msg);
}


blah.rs

mod blah2;
pub fn doit() {
    blah2::doit();
}


blah2.rs

mod utils { pub mod logging; }
pub fn doit() {
    utils::logging::trace("Blah2 invoked");
}


However, this produces an error:

error[E0583]: file not found for module logging
--> src/main.rs:1:21
|
1 | mod utils { pub mod logging; }
| ^^^^^^^
|
= help: name the file either logging.rs or logging/mod.rs inside the directory "src/utils"


It appears that importing down the path, i.e. from main to module1/blah.rs works, and importing peers, i.e. blah2 from blah works, but importing from the parent scope doesn't.

If I use the magical #[path] directive, I can make this work:

blah2.rs

#[path="../utils/logging.rs"]
mod logging;

pub fn doit() {
    logging::trace("Blah2 invoked");
}


Do I really have to manually use relative file paths to import something from a parent scope level? Isn't there some better way of doing this in Rust?

In Python, you use from .blah import x for the local scope, but if you want to access an absolute path you can use from project.namespace.blah import x.

Solution

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 { } } 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");
}

Code Snippets

// 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();
}
mod utils {
    pub mod logging;
}

mod module1 {
    pub mod blah;
}

fn main() {
    utils::logging::trace("Logging works");
    module1::blah::doit();
}
pub fn trace(msg: &str) { 
    println!(": {}\n", msg); 
}
mod blah2;

pub fn doit() {
    blah2::doit();
}
// this is the only change

// Rust 2015
// use utils; 

// Rust 2018    
use crate::utils;

pub fn doit() {
    utils::logging::trace("Blah2 invoked");
}

Context

Stack Overflow Q#20922091, score: 49

Revisions (0)

No revisions yet.