Modules

As the emulator grows, keeping everything in main.rs becomes unmanageable. Modules let you split code into files while controlling what is visible from the outside.

Declaring a module

In main.rs:

#![allow(unused)]
fn main() {
mod cpu;
mod memory;
mod display;
}

mod cpu; tells the compiler: "find the module named cpu in the filesystem." The compiler looks in two places:

DeclarationCompiler looks for
mod cpu;src/cpu.rs or src/cpu/mod.rs
mod memory;src/memory.rs or src/memory/mod.rs

Both forms are equivalent. The folder form (cpu/mod.rs) is preferred when the module has submodules — like when we add a tests.rs file inside the cpu/ directory.

Submodules and test files

When src/cpu/mod.rs contains:

#![allow(unused)]
fn main() {
#[cfg(test)]
mod tests;
}

The compiler looks for src/cpu/tests.rs. The full path of this module is cpu::tests. This is how we keep test code out of the main module without losing access to the internals being tested.

Bringing items into scope

#![allow(unused)]
fn main() {
// full path — always works
let cpu = cpu::Cpu::new();

// use — brings an item into the local scope
use cpu::Cpu;
let cpu = Cpu::new();
}

super in test files

Inside src/cpu/tests.rs:

#![allow(unused)]
fn main() {
use super::*;  // import everything from cpu/mod.rs
}

super refers to the parent module. This gives the test file access to Cpu, execute(), and anything else in cpu/mod.rs — without needing to know the full path crate::cpu::Cpu.

Visibility rules

Items are private by default:

#![allow(unused)]
fn main() {
pub struct Cpu { ... }       // visible everywhere
pub fn step(&mut self) { }   // visible everywhere
fn execute(&mut self) { }    // private — only inside cpu/mod.rs
}

A child module can always see its parent's private items. The reverse is not true.

The module tree for this project

crate (main.rs)
├── cpu (src/cpu/mod.rs)
│   └── tests (src/cpu/tests.rs)
├── memory (src/memory/mod.rs)
│   └── tests (src/memory/tests.rs)
└── display (src/display/mod.rs)

Related Rust concepts: Module visibility uses pub which is introduced in Slices and Ranges. The #[cfg(test)] attribute that gates the test module is covered in Attributes and cfg.