Jump to content
Main menu
Main menu
move to sidebar
hide
Navigation
Main page
Recent changes
Random page
Help about MediaWiki
Special pages
JOHNWICK
Search
Search
Appearance
Create account
Log in
Personal tools
Create account
Log in
Pages for logged out editors
learn more
Contributions
Talk
Editing
Modules
Page
Discussion
English
Read
Edit
View history
Tools
Tools
move to sidebar
hide
Actions
Read
Edit
View history
General
What links here
Related changes
Page information
Appearance
move to sidebar
hide
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
I missed publishing yesterdayâs writeup but here we are. If you missed our discussion on smart pointers check it out below. Smart Pointers Yesterday we covered Closures, why they are important and edge cases to watch when working with them. If you missed it⌠medium.com Rustâs module system is just about organizing your code into logical, reusable pieces. Think of it like organizing a messy toolbox, sockets in one drawer, wrenches in another, and screwdrivers neatly arranged. Without modules, your growing codebase can becomes a chaotic mess and often youll need to separate functions. Modules is a way to group related code, functions, structs, enums, and more, into a single unit. Itâs like a folder in your project that holds pieces of functionality together. Modules control visibility (whatâs public or private), help avoid naming conflicts, and make your code easier to navigate. Imagine youâre working on a backend of a platform. Youâve got code for handling user accounts, processing payments, and managing inventory. Without modules, all these functions and types would live in one giant file or directory, making it hard to find anything. Modules let you split this into users, payments, and inventory modules, so each part is self-contained and easier to work with. Hereâs a simple example of a module structure for such a backend. <pre> mod users { pub struct User { id: u32, name: String, } pub fn create_user(name: &str) -> User { User { id: generate_id(), name: name.to_string(), } } fn generate_id() -> u32 { // Simulate fetching an ID from a database 42 } } fn main() { let user = users::create_user("Alice"); println!("User: {} (ID: {})", user.name, user.id); } </pre> In this code above, the users module groups user-related functionality. The pub keyword makes User and create_user accessible outside the module, while generate_id is private, hidden from the outside world. Such a setup mirrors how youâd organize a real project, exposing only whatâs necessary (like an API) while keeping internal details private. Declaring and Using Modules Modules can be defined in a single file using the mod keyword, as shown in the code above, or split across multiple files for larger projects. Letâs say your project grows, and you want to separate the users module into its own file. Hereâs how youâd do it. src/ âââ main.rs âââ users.rs In main.rs, you declare the module: <pre> // Declares the users module, which Rust looks for in users.rs mod users; fn main() { let user = users::create_user("Bob"); println!("User: {} (ID: {})", user.name, user.id); } And in users.rs, you define the module: pub struct User { pub id: u32, pub name: String, } pub fn create_user(name: &str) -> User { User { id: generate_id(), name: name.to_string(), } } fn generate_id() -> u32 { 42 // Simulate database ID generation } </pre> When you run main.rs, Rust automatically loads the users module from users.rs. This is a common pattern, where each module lives in its own file to keep things organized. For example, a payment processing team might work on payments.rs, while the inventory team works on inventory.rs, and main.rs ties it all together. The connection here is seamless , you just use users::create_user to access the function. Visibility and Encapsulation One of the most practical aspects of modules is controlling visibility with pub. In some scenario where youâre building a library or API and some parts should be exposed to users, but others should remain internal. For instance, in the users module above, create_user and User are public, but generate_id is private because itâs an implementation detail. Consider a payment processing module below. <pre> mod payments { pub fn process_payment(amount: f64, user_id: u32) -> Result<String, String> { if validate_payment(amount, user_id) { Ok(format!("Payment of {} processed for user {}", amount, user_id)) } else { Err("Payment validation failed".to_string()) } } fn validate_payment(amount: f64, user_id: u32) -> bool { // Internal logic to check payment details amount > 0.0 && user_id != 0 } } </pre> In the code above, process_payment is public, so other parts of the program (or external users, if itâs a library) can call that function. But validate_payment is private, keeping the validation logic hidden. This can be critical in cases, where you want to protect internal logic from misuse or accidental dependency. Nested Modules and Paths Another nice thing is that modules can be nested to create a hierarchy, which is super useful for organizing complex projects. Suppose we want to extend our users module with submodules for authentication and profiles: <pre> mod users { pub mod auth { pub fn login(username: &str, password: &str) -> bool { // Simulate authentication here username == "admin" && password == "secret" } } pub mod profile { pub struct Profile { pub bio: String, pub email: String, } pub fn update_profile(user_id: u32, bio: &str, email: &str) -> Profile { Profile { bio: bio.to_string(), email: email.to_string(), } } } } fn main() { if users::auth::login("admin", "secret") { let profile = users::profile::update_profile(42, "Rust enthusiast", "admin@example.com"); println!("Profile updated: {} ({})", profile.bio, profile.email); } } </pre> In our code above, users contains auth and profile submodules. You access them with paths like users::auth::login. This can be common in large projects, like a web framework where you might have framework::http::request and framework::http::response. Nested modules keep related functionality grouped while allowing fine-grained control over visibility. In a real job, for example, if youâre tasked for example with building the authentication system, youâd work in the auth submodule, while someone else handles profile. The module hierarchy ensures your changes donât accidentally break other parts of the system. The use Keyword and Path Management More often as project grows, typing long paths like users::auth::login can get tedious. The use keyword lets you bring items into scope for cleaner code. Hereâs an example: <pre> use users::auth::login; use users::profile::update_profile; fn main() { if login("admin", "secret") { let profile = update_profile(42, "Rust lover", "admin@example.com"); println!("Profile: {} ({})", profile.bio, profile.email); } } </pre> See in our code above how we call login and update_profile directly. This is a lifesaver in real-world projects with deep module hierarchies. You can also use use to create aliases or avoid naming conflicts. Imagine two modules with conflicting function names: <pre> mod inventory { pub fn update(id: u32) { println!("Updating inventory item {}", id); } } mod orders { pub fn update(id: u32) { println!("Updating order {}", id); } } use inventory::update as update_inventory; use orders::update as update_order; fn main() { update_inventory(123); update_order(456); } </pre> The code above could be in a case when integrating third-party libraries or working on a team where different modules might have functions with the same name. Aliases keep things clear and prevent errors. Here are some tips for using modules effectively in real-world Rust projects: * Each module should have a clear purpose, like users::auth for authentication or payments for payment processing. This makes it easier for team members to find and work on specific features. * Break down complex modules into submodules. For example, a database module might have query, connection, and schema submodules to separate concerns. * Be intentional about pub. Expose only whatâs necessary to prevent external code from depending on unstable internal functions. For instance, in the logger example, get_timestamp is private to avoid external misuse. * In large projects, place each module in its own file or directory. For example, a database module might have a directory with mod.rs, query.rs, and connection.rs for clarity. * Write unit tests for each module in its file (e.g., in users.rs for the users module). This ensures bugs in one module donât affect others, a common issue in collaborative projects. That was all for today. Read the full article here: https://medium.com/rustaceans/modules-7afe20b7f6e1
Summary:
Please note that all contributions to JOHNWICK may be edited, altered, or removed by other contributors. If you do not want your writing to be edited mercilessly, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource (see
JOHNWICK:Copyrights
for details).
Do not submit copyrighted work without permission!
Cancel
Editing help
(opens in new window)
Search
Search
Editing
Modules
Add topic