Journey in Rust: Refactoring and the Module System in Rust - Part 7

Journey in Rust: Refactoring and the Module System in Rust - Part 7

Even though our project does what it needs to, it looks like a jumbled mess. Although we split the code into functions in the last few chapters, it is still messy, to say the least. We need to split this code into modules. This will make our code more readable and easier to maintain.

Still don't understand why we need refactoring?

Picture this: you've just built a glorious sandcastle, complete with towers, moats, and flags. πŸ°πŸ–οΈ Suddenly, you realize you forgot to add a super cool drawbridge! 😱 Do you tear down the entire castle to fix it? Of course not! Instead, you carefully modify your sand masterpiece, preserving its beauty while adding awesomeness. That, my friends, is the spirit of refactoring! πŸš€βœ¨

But why do we need refactoring? πŸ€”

  • Clarity, my dear Watson! πŸ•΅οΈβ€β™‚οΈ Refactoring improves code readability, making it easier for you and your fellow code detectives to navigate the complex labyrinth of logic! πŸ§©πŸ—ΊοΈ

  • Upgrade your code-robatics! πŸ€Έβ€β™€οΈ Refactoring can enhance your code's performance, turning it into a nimble gymnast that leaps, flips, and twirls through tasks with ease. πŸ’ƒπŸš€

  • Keep it DRY! 🌡 No, we're not talking about parched landscapes. DRY stands for "Don't Repeat Yourself," and refactoring helps eliminate repetitive code, turning your desert of duplication into a refreshing oasis of efficiency! 🏜️🌴

  • Bug squashing! πŸ›πŸ”¨ Refactoring is like calling in an elite team of exterminators to help you hunt down and eliminate those pesky code bugs. The cleaner your code, the fewer places for creepy crawlies to hide! 🐜🚫

How to refactor code in Rust?

Refactoring in Rust is a piece of cake! 🍰🍰🍰 Just follow these simple steps, and you'll be on your way to a cleaner, more efficient codebase in no time! πŸš€πŸš€πŸš€

Introducing Rust modules, the fantastic way to group your related functions, structs, and enums into neat little compartments! 🎁 Imagine your code like a huge wardrobe, and modules are those cute little organizers that keep everything tidy and accessible. πŸ§₯πŸ‘šπŸ‘–

Creating a module in Rust is as easy as pie (or should we say "pizza"? πŸ•). Just use the mod keyword, followed by your module's name and a block to hold all your awesome code! πŸ’» You can also define a module in a separate file, making it the perfect long-distance relationship for your code. πŸ’˜πŸ“

// In the same file
mod my_module {
    // module contents
}

// In a separate file (my_module.rs)
mod my_module;

Nested modules? No problem! 🐣 Rust lets you create a cozy hierarchy of modules, making your code feel like a lovely family tree. πŸŒ³πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦

mod outer_module {
    pub mod inner_module {
        // module contents
    }
}

Now, let's talk privacy. 🀫 Rust modules know when to keep a secret! By default, items in a module are private and can't be accessed by nosy neighbors. But if you want to share the fun, just add the pub keyword, and voilΓ ! Your function is now the life of the party! πŸŽˆπŸŽ‰

mod my_module {
    pub fn public_function() {
        // This function can be accessed outside the module
    }

    fn private_function() {
        // This function can only be accessed within the module
    }
}

Ready to use items from other modules? Just invite them with the use keyword, and they'll join the celebration in no time! πŸ’ŒπŸ“¬, you can also use as to give the module a nickname.

// In the same file
mod my_module {
    pub fn my_function() {}
}

use my_module::my_function;
// or
use my_module::my_function as my_alias;

fn main() {
    my_function();
    my_alias();
}

Paths in Rust are like the directions to a secret party: follow them correctly, and you'll find the treasure (in this case, your module items)! πŸ—ΊοΈπŸ“

// Absolute path
use crate::my_module::my_function;

// Relative path
use self::my_module::my_function;

So, what are you waiting for? Hop on the Rust module party train and turn your code into a well-organized, clean, and joyful celebration of functionality! πŸš‚πŸ₯³πŸ’»


Let's refactor our code

Step 1: Create the new module files πŸ“

To begin, let's create four new files in the same directory as main.rs:

  1. cli.rs: Handles command-line interface (CLI) arguments and parsing.

  2. api.rs: Manages API requests and responses.

  3. os.rs: Provides system-related utilities.

  4. commands.rs: Executes external commands.

Step 2: Refactor cli.rs 🧩

Move the Args, Commands, and related structures and enums from main.rs to the new cli.rs file. Then, add pub before each structure, enum, and function you'll need to access from main.rs.

In main.rs, add the following import statement to the top of the file:

mod cli;
use cli::{Args, Commands};

Step 3: Refactor api.rs 🌐

Move all API-related functions, like get_response, get_header, get_body, get_system_message, and get_api_key, as well as the ApiResponse structure, to the new api.rs file. Remember to add pub before each structure and function you'll need to access from main.rs.

In main.rs, add the following import statement:

mod api;
use api::{get_response, ApiResponse};

Step 4: Refactor os.rs πŸ’»

Move the get_pretty_name, get_default_tokens, and get_os functions to the new os.rs file. As before, add pub before each function you'll need to access from main.rs.

In main.rs, add the following import statement:

mod os;
use os::{get_default_tokens, get_os};

Step 5: Refactor commands.rs βš™οΈ

Move the Instructions structure and the handle_external_commands function to the new commands.rs file. Don't forget to add pub before each structure and function you'll need to access from main.rs.

In main.rs, add the following import statement:

mod commands;
use commands::{handle_external_commands, Instructions};

Step 6: Clean up and update dependencies 🧼

With everything moved to their respective modules, double-check that you've updated all the dependencies in main.rs. Ensure you're importing and using the necessary structures and functions from each module.

Congratulations! You've successfully refactored your Rust codebase into a well-organized, modular structure. Now you can navigate and update your code with ease. Keep up the great work, Rustaceans! πŸ¦€πŸŽ‰

πŸ¦€ Fun Fact Alert! 🚨 Did you know that Rust, the awesome systems programming language we all know and love, was actually named after a fungus? Yes, you read that right! The name "Rust" was inspired by a plant pathogen called "rust fungus" that can cause devastating damage to crops 🌾. It turns out the creators of Rust wanted to symbolize how their language can "eat away" at the challenges and complexities of writing safe, concurrent systems codeβ€”just like the fungus can eat away at plants. So, next time you're working with Rust, remember that you're harnessing the power of a mighty fungus to conquer the world of programming! πŸ„πŸ’ͺ🌍

I guess this will be it for today. I will be back with another post soon. If you have any questions or suggestions, feel free to leave a comment below. Thanks for reading! πŸ™

Cover: Bhupesh

πŸ”— Repository: termoil


Β