Do you remember the first time you made a project with more than one source file? Was it with headers, with classes...or, with modules?

Modules have certain traits. They allow various items to coexist in a unit, without being bound to a class. A module file generally has its interface (outward-facing public parts) on top, and its implementation (dirty inner details) on the bottom.

Understanding Rust's modules comes from understanding the keywords mod , pub , and use . To state them simply:

mod declares a module.

declares a module. pub exposes.

exposes. use pulls things in.

Let's begin with an example. Modules can be defined two ways. First in place:



// main.rs mod food { pub fn eat () { println! ( "Good." ); } } fn main () { food :: eat (); }

Second, as a separate file:



// main.rs mod food ; fn main () { food :: eat (); } // food.rs or food/mod.rs pub fn eat () { println! ( "Yum." ); }

So more specifically:

mod X means: let there be a module X, defined either here, in braces, or in a separate file named X.rs or X/mod.rs .

pub fn eat makes eat visible to main. Without pub , main would not be able to call eat .

Note that without mod food in main, Rust would ignore food.rs entirely.

Next, let's expand the above to something more deeply nested:



// main.rs mod food { pub mod lunch { pub fn eat () { println! ( "Take a break." ); } } } fn main () { food :: lunch :: eat (); }

Or, as multiple files. Note the use of mod.rs , a special filename:



// main.rs mod food ; fn main () { food :: lunch :: eat (); } // food/mod.rs pub mod lunch ; // food/lunch.rs pub fn eat () { println! ( "Hamburger." ); }

Does this look like you expected? Notice how both fn eat and mod lunch are made pub . They must be carried up this way, one level at a time.

And of course, anything without pub is invisible outside its module.

Perhaps surprisingly, pub treats struct members and methods on an individual basis.



// food.rs struct Meal { pub taste : String , price : u32 , // cannot see price } impl Meal { pub fn eat ( & self ) { println! ( "I taste {}." , self .taste ); } fn purchase ( & self ) { // cannot call purchase directly println! ( "Spent ${}. Ouch." , self .price ); } }

Having a private variable price means that you cannot make a Meal outside of food . A constructor method can help with that.



// food.rs impl Meal { pub fn fancy () -> Self { Meal { taste : String :: from ( "miracles on plate" ), price : 44100 , } } } // main.rs mod food ; fn main () { let lunch = food :: Meal :: fancy (); lunch .eat (); }

So, to recap:

pub exposes an item, either on module or struct level, to its surrounding level.

Do note that Meal::fancy could also be named Meal::new , or even other things.

Next, let's examine use to complete our set of three. use is a tool that pulls in words to reduce visual clutter and finger work.



mod food ; use food :: Meal ; fn main () { let lunch = Meal :: fancy (); lunch .eat (); }

Or even:



mod food ; use food :: Meal :: fancy ; fn main () { let lunch = fancy (); lunch .eat (); }

Pulling in fancy directly is an extreme example, but it does work. In the same way that pub can apply to structs, so can use .

A wrinkle with use is that it has absolute paths. To understand paths, consider this non-working example:



mod food { fn cheaper ( a : & Meal , b : & Meal ) -> u32 { std :: cmp :: min ( a .price , b .price ) } }

This fails because std::cmp is the same as writing self::std::cmp , self here being food . It's a relative path. So, let's use an absolute path by adding :: to the start.



mod food { fn cheaper ( a : & Meal , b : & Meal ) -> u32 { :: std :: cmp :: min ( a .price , b .price ) } }

This works, but the use keyword is also an absolute path, and can reduce typing in the long run.



mod food { use std ; fn cheaper ( a : & Meal , b : & Meal ) -> u32 { std :: cmp :: min ( a .cost , b .cost ) } }

Here's another way of doing it. Yes, use does respect its scope.



mod food { fn cheaper ( a : & Meal , b : & Meal ) -> u32 { use std :: cmp :: min ; min ( a .cost , b .cost ) } }

There is some light alternate syntax for use as well, in order to pull in multiple things.



mod food { pub mod bread { pub struct Slice {} pub struct Loaf {} } pub struct Plate {} pub struct Napkin {} } use food :: bread :: * ; // pulls in Slice and Loaf use food ::{ Plate , Napkin };

So, to be specific:

use brings module or struct items into the current scope from an absolute path.

Also note that pub use can be used as a flattening method.



mod food { pub mod breakfast { pub mod cereal { pub fn eat () { println! ( "Snip, crankle, porp." ); } } } pub use self :: breakfast :: cereal ; } fn main () { food :: cereal :: eat (); }

This enables you to divide into further modules for organization, without adding a burden to the outside world.

Overall, it is better to be more explicit (avoid too much * ) and use judiciously. It's better to know where things are coming from when you see them later, but added visual clutter and typing are no fun.

To recap:

mod declares a module.

declares a module. pub exposes an item by a single level.

exposes an item by a single level. use brings things from an absolute path to the current scope.

There is more, but I hope I have helped get things started. Let me know if you're interested in a second part.