Understanding Code Smells in Rust

Introduction

Welcome to the very first lesson of the "Clean Code with Multiple Structs and Traits in Rust" course! 🎉 This course aims to guide you in writing code that's easy to understand, maintain, and enhance. Within the broader scope of clean coding, effective struct collaboration is crucial for building well-structured applications. In this lesson, we will delve into the intricacies of struct collaboration and coupling — key factors that can make or break the maintainability of your software. Specifically, we'll address some common "code smells" that indicate problems in struct interactions and explore ways to resolve them.

Overview of Struct Collaboration Challenges

Let's explore the challenges of struct collaboration by focusing on four common code smells:

  • Feature Envy: This occurs when a method in one struct is overly interested in methods or data in another struct.
  • Inappropriate Intimacy: Describes a situation where two structs are too closely intertwined, sharing private details.
  • Message Chains: Refer to sequences of method calls across several instances, indicating a lack of clear abstraction.
  • Middle Man: Exists when a struct primarily delegates its behavior to another struct without adding functionality.

Understanding these code smells will enable you to improve your struct designs, resulting in cleaner and more maintainable code.

Problems Arising During Struct Collaboration

These code smells can significantly impact system design and maintainability. Let's consider their implications:

  • They can lead to tightly coupled structs, making them difficult to modify or extend.
  • Code readability decreases, as it becomes unclear which struct is responsible for which functionality.

Addressing these issues often results in code that is not only easier to read but also more flexible and scalable. Tackling these problems can substantially improve software architecture, making it more robust and adaptable.

Feature Envy

Feature Envy occurs when a method in one struct is more interested in the fields or methods of another struct than its own. Here's an example:

struct Item {
    price: f64,
    quantity: f64,
}

struct ShoppingCart {
    items: Vec<Item>,
}

impl ShoppingCart {
    fn new(items: Vec<Item>) -> ShoppingCart {
        ShoppingCart { items }
    }

    fn calculate_total_price(&self) -> f64 {
        self.items.iter().map(|item| item.price * item.quantity).sum()
    }
}

In this scenario, calculate_total_price in ShoppingCart overly accesses data from Item, indicating feature envy.

To refactor, consider moving the logic to the Item struct:

struct Item {
    price: f64,
    quantity: f64,
}

impl Item {
    fn calculate_total(&self) -> f64 {
        self.price * self.quantity
    }
}

struct ShoppingCart {
    items: Vec<Item>,
}

impl ShoppingCart {
    fn new(items: Vec<Item>) -> ShoppingCart {
        ShoppingCart { items }
    }

    fn calculate_total_price(&self) -> f64 {
        self.items.iter().map(Item::calculate_total).sum()
    }
}

Now, each Item calculates its own total, reducing dependency and distributing responsibility appropriately. ✔️

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal