Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Modules

As you write larger Rust programs, you will start to accumulate large amounts of code. Depending on the complexity of your program, you may have hundreds or thousands of lines of code. Rather than having all this code in a single, large file, you can split it apart into modules.

You can use modules in Rust to separate code into different source files. This helps keep correlated code together, that shares a common theme or purpose.

Modules can contain Rust structs, traits, trait implementations, variables, and functions.

Use Cases for Rust Modules

As a developer, it's entirely your decision about how to split your code up into modules. However, there are some general use cases that you might want to consider using modules for:

  • Code related to working with different storage engines: MySQL, Postgres, Redis, Amazon S3, etc.
  • Define various aspects of a REST API: managing customers, products, payment methods, media files
  • Separate modules for each type of widget you are manufacturing, eg. steering wheel, shift knob, turn signal lever

Define Inline Rust Module

One of the easiest ways to define a module is to simply add an inline module block to your existing program. If you're building a simple Rust program with main.rs, you might add a Customer module to it. Here's a simple, contrived example of an inline module that provides a couple of functions.

#![allow(unused)]
fn main() {
mod customer {
  pub fn create_customer() {
    println!("Creating new customer ...");
  }

  pub fn deactivate_customer() {
    println!("Deactivating customer account ...");
  }
}
}

â„šī¸ NOTE: If you're wondering what the pub keyword is, check out the "Item Visibility" section below.

Now that you've defined a couple of functions inside of a Rust module, you can call those functions from other parts of your program. In your main.rs file, if you'd like to reference either of these functions, you can simply prefix them with the module's name, similar to the following example.

fn main() {
  customer::create_customer();
}

If you'd like to try this out for yourself, why not use this example on the Rust Playground?

đŸĻ€ Rust Playground Example

Nested Rust Modules

Rust supports multiple levels of the module hierarchy. You can create a module inside of another module. This helps you to establish groupings, as necessary, for your program.

#![allow(unused)]
fn main() {
mod database_adapters {
  pub mod mysql {
      pub static DB_NAME: &'static str = "MySQL";
  }
  
  pub mod postgres {
      pub static DB_NAME: &'static str = "Postgres";
  }
  
  pub mod mssql {
      pub static DB_NAME: &'static str = "Microsoft SQL Server";
  }
}
}

If the child modules, and their members, are not marked with pub visibility, then you will receive a compilation error when you try to access them outside of the child modules themselves. For example, if I remove pub from the mssql module, and try to compile a program that references the DB_NAME variable in that module, then this error would appear.

⛔ error[E0603]: module mssql is private

If you'd like to try this out for yourself, why not use this example on the Rust Playground?

đŸĻ€ Rust Playground Example

Define Module as Separate File

In a Rust project where you have main.rs, let's say you want to add some functionality pertaining to people. You decide to define a basic Person struct, for starters.

#![allow(unused)]
fn main() {
pub struct Person<'p> {
  first_name: &'p str,
  last_name: &'p str,
  birth_date: &'p str,
}
}

Instead of placing this in main.rs, place this struct definition into a new file, in the same directory as main.rs, named person.rs. Then, go back to main.rs and add the following line somewhere near the top of the file.

#![allow(unused)]
fn main() {
mod person;
}

This statement will tell the Rust compiler to look for person.rs and include it in the scope of the main.rs file. You should now be able to instantiate the Person struct from your main() function. Specify the path to the struct, using module::struct syntax, and then add the curly braces to pass in the initial field values.

fn main() {
  let p1 = person::Person { first_name: "Trevor", last_name: "Sullivan", birth_date: "2000-02-03" };
}

Item Visibility

By default, when you define any struct, function, variable, or trait (collectively, "items") in a Rust module, the item will have a private visibility. This means that the item cannot be used outside of that module. If an item needs to be used outside of the module, then it must use the pub visibility modifier. Simply prefix the item's name with the pub modifier to make it public, meaning it will be visible outside the containing module.

Here's how you'd make a Rust function visible outside its module:

#![allow(unused)]
fn main() {
person.rs
pub struct Person {
  ...
}
}

Here's how you'd make a trait public from a module.

#![allow(unused)]
fn main() {
pub trait WalkOrRun {
  fn walk(distance: u8);
  fn run(speed: u8, time: u8);
}
}