Organizing Code: Rust Modules vs C# Namespaces

Welcome to Day 22! After a week of wrestling with data modeling, it’s time to talk about something near and dear to every developer’s heart: keeping your code organized (and your sanity intact).

If you’re a C# developer, you’ve lived in the world of namespaces, public and internal access modifiers, and .cs files that can stack up faster than your coffee cups during crunch time. In Rust, the story is a little different, but delightfully simple once you get the hang of it.

C# Namespaces: The Familiar Territory

In C#, you group your code into namespaces, like this:

namespace MyApp.Services

public class EmailService
{
    public void SendEmail(string address, string message)
    {
        Console.WriteLine($"Sending '{message}' to {address}");
    }
}

C# gives you public, internal, protected, and private as tools to control access. Plus, namespaces can be arbitrarily deep, independent of file structure. Your project might have folders that kind of line up with namespaces, but they don’t have to.

Rust Modules: Organization That Matches Your Files

In Rust, code organization is driven by modules, which are explicitly tied to your folder and file structure. Here’s the core idea:

  • mod declares a module.
  • pub makes items public outside the module.
  • Files and directories mirror the module structure.

Example:

// src/main.rs
mod services;

fn main() {
    services::send_email("woody@dev.com", "Hello from Rust!");
}

And in src/services.rs:

pub fn send_email(address: &str, message: &str) {
    println!("Sending '{}' to {}", message, address);
}

Notice how the module directly maps to the filename? Clean and predictable.

Going Deeper: Nested Modules

Rust also lets you create submodules using folders and mod.rs files, like this:

src/
├── main.rs
└── services/
    ├── mod.rs
    └── email.rs

In mod.rs:

pub mod email;

In email.rs:

pub fn send_email(address: &str, message: &str) {
    println!("Sending '{}' to {}", message, address);
}

In main.rs:

mod services;

fn main() {
    services::email::send_email("woody@dev.com", "Hello again!");
}

Access Control: pub vs. private by Default

In Rust, everything is private by default, unlike C#, where classes and methods are often public unless you say otherwise.

  • pub: Makes things visible outside the module.
  • No pub: Private to the module.

If you need to expose something to just sibling modules (like C#’s internal), Rust offers pub(crate) for crate-wide visibility.

pub(crate) fn internal_tool() {
    println!("Only visible within this crate!");
}

C#’s Flexibility vs. Rust’s Structure

C# lets you throw namespaces around however you want. Files don’t need to match namespaces. Rust, on the other hand, encourages clarity by enforcing file/module alignment. This can feel restrictive at first, but once you get into the flow, it makes code navigation and organization much easier.

Why This Matters

In C#, you might find yourself hunting through files to match up namespaces and folders. Rust’s “your modules are your files” approach keeps things straightforward:

  • Fewer surprises.
  • Easier to onboard new developers.
  • Access rules are enforced at compile-time, not just by convention.

Wrapping It Up

Rust’s module system is like that friend who insists on labeling their spice jars and alphabetizing their bookshelf. At first, it feels a little rigid, but when you’re looking for the paprika (or that one helper function), you’re really glad they did it.

Tomorrow, we’ll dig into crates and dependencies. Where Rust’s package manager, Cargo, really shines. See you then!

Share:

Leave a reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.