75

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.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Doug
  • 32,844
  • 38
  • 166
  • 222

5 Answers5

56

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.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Doug
  • 32,844
  • 38
  • 166
  • 222
46

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:

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");
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
huon
  • 94,605
  • 21
  • 231
  • 225
  • 11
    Please explain how to split this into multiple files; the example above is a *trivial example*; I want multiple files, that access functions defined in other modules in other files. Come on, I'm not trying to do something weird here. I want the logging module inside a separate file, in a separate directory, and I want the blah modules likewise, *as I described in my file layout in my question*. "Just stick it all in one file and use 'use utils' doesn't answer anything. – Doug Jan 05 '14 at 10:11
  • I did describe how to split it into multiple files, second last paragraph. – huon Jan 05 '14 at 11:03
  • Your directory structure you already have should work ok, you *just* need to replace the `mod utils { ... }` in `blah2.rs` with `use utils;`. – huon Jan 05 '14 at 11:03
  • 1
    "unresolved import. maybe a missing extern mod utils" – Doug Jan 05 '14 at 11:05
  • I've added code to the question with your required layout. (It works for me.) – huon Jan 05 '14 at 11:12
  • Were you changing `main.rs` to have `use utils` too? – huon Jan 05 '14 at 11:19
  • I didn't have a mod utils; in my main, that was the problem. – Doug Jan 05 '14 at 11:55
  • 1
    This does not seem to work anymore. layout: `Cargo.lock Cargo.toml src/main.rs src/module1/blah.rs src/module1/blah2.rs src/utils/logging.rs`. Example errors: https://i.imgsafe.org/342df1e954.png – The Unfun Cat Oct 28 '16 at 12:25
  • Even if I change the code in blah.rs to `use blah2;` I get the error: `error[E0432]: unresolved import blah2. There is no blah2 in the crate root --> src/module1/blah.rs:1:5 | 1 | use blah2; | ^^^^^` – The Unfun Cat Oct 28 '16 at 12:27
8

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!

JustDave
  • 516
  • 1
  • 8
  • 18
  • Upvoted for simplicity. Much more relatable answer, especially given the very helpful github link. I don't understand why this import stuff is so difficult in rust vs other languages and why so much documentation is needed for such a basic feature. – dim_voly Sep 30 '22 at 00:52
2

Answers here were unclear for me so I will put my two cents for future Rustaceans.

All you need to do is to declare all files via mod in src/main.rs (and fn main of course).

// src/main.rs

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

fn main () {
    module1::blah::doit();
}

Make everything public with pub you need to use externally (copy-pasted original src/utils/logging.rs). And then simply use declared modules via crate::.

// src/module1/blah.rs

use crate::utils::logging;
// or `use crate::utils` and `utils::logging::("log")`, however you like

pub fn doit() {
  logging::trace("Logging works");
}

Think of crate:: as ~/ where ~ is the src folder of your cargo project. There's also a keyword super:: to access from current directory (like ./).


p.s. I shuffled functions a bit for a cleaner answer.

SpekalsG3
  • 155
  • 1
  • 8
0

If you create a file called mod.rs, rustc will look at it when importing a module. I would suggest that you create the file src/utils/mod.rs, and make its contents look something like this:

pub mod logging;

Then, in main.rs, add a statement like this:

use utils::logging;

and call it with

logging::trace(...);

or you could do

use utils::logging::trace;

...

trace(...);

Basically, declare your module in the mod.rs file, and use it in your source files.

a_m0d
  • 12,034
  • 15
  • 57
  • 79
  • You've misunderstood the question I think. Calling utils::logging from main.rs already works. I'm not talking about trying to import a module by name, I'm trying to import a module from the *parent scope*. Ie, I'm trying to use utils::logging, from inside module1::blah; utils is in the scope of the parent (main), but it is not in scope of the child (module1), which is why I must use the #[path] directive, because by default trying to invoke 'utils::logging' from module1 makes rust look for the file /src/module1/utils/logging.rs, not /src/utils/logging.rs – Doug Jan 05 '14 at 04:21
  • @Doug by putting a `mod utils { ... }` in `blah2` you are creating a whole new module called `module1::blah::blah2::utils::logging` that is *different* to the top level `utils::logging` despite coming from the same file. You use `use` to import a module into scope without inserting a new copy of it into the module tree. Rust module system is not file based, it just happens to let you split submodules into different files, you can inline the contents of each module into the `mod` statement (i.e. `mod foo { }`) and it will behave identically. – huon Jan 05 '14 at 05:19
  • 1
    @dbaupp I tried using use, but I get errors like 'unresolved import. maybe a missing `extern mod utils`'. Could you possible post a solution that is correctly done for the trivial example in the question? I was under the impression that 'extern mod blah' was for calling external libraries; that's absolutely not what I'm trying to achieve here (...is it? O_o) – Doug Jan 05 '14 at 06:23