Encapsulation in Rust and Python
Antonov Mike
Posted on April 5, 2024
Disclaimer
I am sure that there are inaccuracies, and maybe even mistakes in this text. I am always happy to receive objective criticism in an acceptable form.
Introduction
Encapsulation is a fundamental concept in object-oriented programming that allows for the bundling of data with the methods that operate on that data. Rust and Python both support encapsulation, which means you can control the access to the attributes and methods of a class or a trait using public, private, and protected modifiers. This concept is implemented in different ways in Rust and Python due to languages design and paradigms. In Rust, we use pub
to make methods and structs accessible outside their module because default they are private. In Python, we use double underscores __
to public by default attributes and methods private. Both languages provide mechanisms to control access to the data, ensuring encapsulation.
Encapsulation in Rust
In Rust, encapsulation is achieved through the use of struct
and impl
blocks. A struct
is used to define a data type with named fields, and an impl
block is used to define methods associated with the struct. In Rust, encapsulation is achieved through the use of pub
and pub(crate)
(protected) modifiers. Here's a simple example:
src/employee.rs
// Define a struct with field (it is private by default)
pub struct Account {
// Private fields
balance: f64,
}
impl Account {
// Public method to create a new Account
pub fn new(initial_balance: f64) -> Account {
Account {
balance: initial_balance,
}
}
// Public method to get the balance
pub fn get_balance(&self) -> f64 {
self.balance
}
// Public method to deposit money
pub fn deposit(&mut self, amount: f64) {
self.balance += amount;
}
// Private method to apply fees
fn apply_fees(&mut self) {
const FEE: f64 = 2.5;
self.balance -= FEE;
}
}
src/main.rs
mod employee;
use employee::Account;
fn main() {
let mut account = Account::new(100.0);
account.deposit(50.0);
println!("Balance: {}", account.get_balance());
// This line will not compile because apply_fees is private
account.apply_fees();
}
Attempting to call the apply_fees
method will result in an error:
error[E0624]: method `apply_fees` is private
--> src/main.rs:9:13
|
9 | account.apply_fees();
| ^^^^^^^^^^ private method
|
::: src/employee.rs:26:5
|
26 | fn apply_fees(&mut self) {
| ------------------------ private method defined here
In this Rust example:
In the Rust example, we have a simple Account struct
with a private field called balance. Here’s how it works:
- Struct Definition: We define the
pub struct
Account in the src/employee.rs file. Thebalance
field is private by default (not exposed outside the module). - Public Methods: We provide public methods
new
,get_balance
,deposit
. These methods allow controlled access to the balance field. - Private Method: The
apply_fees
method is marked as private (not accessible outside thestruct
). It deducts a fixed fee from the account balance. - Usage in main.rs: In the main function, we create an Account instance, deposit money, and print the balance. Attempting to call
apply_fees
results in a compilation error since it’s private.
Python Example
Python uses a different approach to encapsulation, primarily through the use of underscores to denote private attributes and methods. However, it's important to note that Python's privacy is more of a convention than a strict enforcement. In Python, all attributes and methods are public by default. To make an attribute or method private, you prefix its name with double underscores (__
). This does not make the attribute or method truly private, but it signals to other developers that it is intended for internal use within the class. The provided Python example demonstrates encapsulation by defining a class with a private attribute (__balance
) and public methods (__init__
, get_balance
, deposit
). The __apply_fees
method is intended to be private, but due to Python's lack of strict access control, it can still be accessed from outside the class, although it is not recommended.
# Define a class with private attributes
class Account:
def __init__(self, initial_balance):
self.__balance = initial_balance # Private attribute
def get_balance(self):
return self.__balance
def deposit(self, amount):
self.__balance += amount
def __apply_fees(self): # Private method
FEE = 2.5
self.__balance -= FEE
# Create an instance of Account
account = Account(100)
account.deposit(50)
print("Balance:", account.get_balance())
# This line will raise an AttributeError because __apply_fees is private
account.__apply_fees()
Attempting to call the apply_fees
method will result in an error:
AttributeError: 'Account' object has no attribute '__apply_fees'
In this Python example:
- Constructor and Private Attribute: The
__init__
method initializes an Account instance with an initial balance. The__balance
attribute is marked as private. - Public Methods: We define public methods:
get_balance
anddeposit
(to add funds). These methods allow controlled access to the private__balance
. - Private Method: The
__apply_fees
method is marked as private. It deducts a fixed fee from the account balance. - Usage: We create an account instance, deposit money, and print the balance. Attempting to call
__apply_fees
raises anAttributeError
due to its private status.
Summary
Both examples demonstrate encapsulation by hiding implementation details and providing controlled access to data and methods. Rust achieves encapsulation through module visibility, while Python uses name mangling and access modifiers
Both Rust and Python support encapsulation, but they implement it in ways that reflect their language design principles and paradigms. Rust's approach is more explicit and enforced by the language, while Python's approach is more flexible but relies on convention and name mangling.
It's important to remember that Python's approach to encapsulation is more about convention and best practices rather than strict language enforcement.
Useful articles:
Encapsulation in Rust
Rust Is Beyond Object-Oriented, Part 1: Intro and Encapsulation, Jimmy Hartzell, 2022-12-12
Object-Orientation in Rust
How to Implement Object-Oriented Programming Concepts in Rust, 2023-04-02
How to write properly encapsulation?, Harry Ying, February 2018
Encapsulation in Python
Encapsulation in Python, Updated on: 2021-08-28
Embedding Python in Rust with WebAssembly, Asen Alexandrov, June 2023
Encapsulation in Python
Python Private Attributes
Characteristics of Object-Oriented Languages, doc.rust-lang.org
Rust + Python:
Integrating Rust into Python, Edward Wright, 2021-04-12
Examples for making rustpython run actual python code
Calling Rust from Python using PyO3
Writing Python inside your Rust code — Part 1, 2020-04-17
RustPython, RustPython
Rust for Python developers: Using Rust to optimize your Python code
PyO3 (Rust bindings for Python)
Musing About Pythonic Design Patterns In Rust, Teddy Rendahl, 2023-07-14
Image created by Bing and edited by me
Other articles about the similarities and differences between Rust and Python
Posted on April 5, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.