Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you use parent module imports in Rust?

Tags:

rust

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.

like image 382
Doug Avatar asked Jan 04 '14 14:01

Doug


People also ask

How do you import a module in Rust?

To import the module, we use the keyword mod , followed by the file name without the . rs extension. When a module is imported, it allows us to use any code that's marked as public in the file we're importing the module from. Note that the module must be imported before we use any of its code.

How do modules work in Rust?

Rust provides a powerful module system that can be used to hierarchically split code in logical units (modules), and manage visibility (public/private) between them. A module is a collection of items: functions, structs, traits, impl blocks, and even other modules.

What is MOD RS in Rust?

But in Rust, folders are not immediately ready to use as modules. You have to add a file named mod.rs in a folder to expose a new module named like that folder. The code in mod.rs is the content of that module. All other files in the folder may in turn be exposed as submodules (more on that below).

What is pub in Rust?

pub(in path) makes an item visible within the provided path . path must be a parent module of the item whose visibility is being declared. pub(crate) makes an item visible within the current crate.


1 Answers

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.

like image 95
Doug Avatar answered Sep 21 '22 14:09

Doug