Disclaimer
This is more my attempt to explain this topic to myself. How successful it is is for more experienced people to judge. Argumented criticism in an acceptable form is welcome as always
Introduction
Composition in programming is a way of building complex structures by combining simple structures. Rust and Python both support composition, which means you can combine the features of different classes or traits to create more complex and powerful classes or types. This approach promotes code reusability and modularity. Let's explore how composition can be used in both languages to model a car rental system.
Rust Example
In Rust, we can use traits and structs to model different aspects of a car rental system. For example, we might have a Car
trait, a Rental
struct, and a Customer
struct.
trait Car {
fn get_make(&self) -> &str;
fn get_model(&self) -> &str;
}
struct Rental {
car: Box<dyn Car>,
customer: String,
rental_days: u32,
}
struct Customer {
name: String,
rentals: Vec<Rental>,
}
struct Sedan {
make: String,
model: String,
}
impl Car for Sedan {
fn get_make(&self) -> &str {
&self.make
}
fn get_model(&self) -> &str {
&self.model
}
}
fn main() {
let sedan = Sedan {
make: "Citroën".to_string(),
model: "C3".to_string(),
};
let rental = Rental {
car: Box::new(sedan),
customer: "John Doe".to_string(),
rental_days: 7,
};
let customer = Customer {
name: "John Doe".to_string(),
rentals: vec![rental],
};
println!("Customer {} rented a {} {} for {} days.",
customer.name,
customer.rentals[1].car.get_make(),
customer.rentals[1].car.get_model(),
customer.rentals[1].rental_days);
}
Output:
$ cargo run
Customer Kurmanjan Datka rented a Citroën C3 for 7 days.
In this Rust example, we define a Car
trait that any car type must implement. We then create a Rental
struct that contains a boxed Car
trait object, allowing for any type that implements Car
to be rented. This demonstrates composition by combining different traits and structs to create a more complex system.
Python Example
In Python, we can achieve a similar effect using classes and inheritance. Python's dynamic typing makes it easy to compose objects from different classes.
from typing import List
# Here we declare classes to use them as types
# later to make our code more readable
class Customer:
pass
class Rental:
pass
class Car:
def __init__(self, make: str, model: str):
self.make = make
self.model = model
def get_make(self) -> str:
return self.make
def get_model(self) -> str:
return self.model
class Rental:
def __init__(self, car: Car, customer: Customer, rental_days: int):
self.car = car
self.customer = customer
self.rental_days = rental_days
class Customer:
def __init__(self, name: str):
self.name = name
self.rentals: List[Rental] = []
def add_rental(self, rental: Rental):
self.rentals.append(rental)
class Sedan(Car):
pass
sedan = Sedan("Citroën", "C3")
customer = Customer("Rita Levi-Montalcini")
rental = Rental(sedan, customer, 7)
customer.add_rental(rental)
print(
f"Customer {customer.name} rented a {customer.rentals[0].car.get_make()} {
customer.rentals[0].car.get_model()} for {customer.rentals[0].rental_days} days."
)
Output:
$ python main.py
Customer Rita Levi-Montalcini rented a Citroën C3 for 7 days.
In the Python example, we define a Car
class with methods to get the make and model. We then create a Rental
class that takes a Car
instance, a customer name, and the number of rental days. The Customer
class contains a list of rentals. By using inheritance (e.g., Sedan
inherits from Car
), we can easily compose different types of cars and rentals.
Both examples demonstrate how composition can be used to build complex systems by combining simpler, reusable components.
So what are the differences?
The difference in composition between Rust and Python lies in how they organize and utilize objects and data structures, as well as in memory management and type mechanisms. While Python supports inheritance, allowing classes to inherit from other classes, Rust does not support inheritance in the same way. Instead, Rust encourages composition through traits and struct composition. This leads to a more modular and flexible design, where components can be combined in various ways to achieve the desired functionality. Python's inheritance model can lead to more tightly coupled designs, whereas Rust's composition model promotes more decoupled and reusable components. Both options have their pros and cons depending on where you use them.
In summary, while both languages support principles of composition, they do so differently, taking into account their unique features in type typing, memory management, and software architecture.
These articles may be useful
- Writing Python like it's Rust
- Inheritance and Composition: A Python OOP Guide
- [Python App: Rental Car Cost Estimator
- ](https://www.youtube.com/watch?v=LcsEf9IdATA)
- design-patterns/strategy
- Rust takes the composition over inheritance approach
- Design patterns strategy
Image created by Bing and edited by me
Top comments (0)