Directory as module

We can also create a directory that represents a module. This approach allows us to have submodules within modules as a file and directory hierarchy. Let's assume that we have a directory,  my_program, that has a module named foo as a file foo.rs. It contains a type called Bar along with foo's functionality. Over time, the Bar APIs have grown in number and we wish to separate them as a submodule. We can model this use case with directory-based modules. 

To demonstrate creating modules as directories, we have created a program in a directory named my_program. It has an entry point in main.rs and a directory named foo. This directory now contains a submodule within it named bar.rs.

Following is the structure of the directory my_program:

+ my_program
└── foo/
└── bar.rs
└── foo.rs
└── main.rs

To let Rust know about bar, we also need to create a sibling file named foo.rs alongside the directory foo/. The foo.rs file will contain mod declarations for any submodules created (here bar.rs) within the directory foo/.

Our bar.rs has the following content:

// my_program/foo/bar.rs

pub struct Bar;

impl Bar {
pub fn hello() {
println!("Hello from Bar !");
}
}

We have a unit struct Bar having an associated method hello. We want to use this API in main.rs.

Note: In the older Rust 2015 edition, submodules don't need a sibling foo.rs alongside the foo directory, and instead use a mod.rs file within foo to convey to the compiler that the directory is a module. Both of these approaches are supported in Rust 2018 edition.

Next, our foo.rs has the following code:

// my_program/foo.rs

mod bar;
pub use self::bar::Bar;

pub fn do_foo() {
println!("Hi from foo!");
}

We added a declaration of the module bar. Following that, we re-exported the item Bar from the module bar. This requires that Bar is defined as pub. The pub use part is how we  re-export an item from a child module to be available from the parent module. Here, we used the self keyword to reference the module itself. Re-exports are mainly a convenience step when writing use statements, which helps remove the clutter when importing an item that is hidden away in nested submodules.

self is a keyword for relative imports. While it's encouraged to use absolute imports using crate, it is much cleaner to use self when re-exporting items from submodules in the parent module.

Finally main.rs uses both modules as:

// my_program/main.rs

mod foo;

use foo::Bar;

fn main() {
foo::do_foo();
Bar::hello();
}

Our main.rs declares foo and then imports the struct Bar. We then invoke the method do_foo from foo and also invoke hello on Bar.

There's more to modules than meets the eye and thus we cover some of the details about them in Chapter 7, Advanced Concepts. With modules explored, let's continue with Cargo.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset