Comparing OOP in Rust, Java, and C++
Object-Oriented Programming (OOP) has been a cornerstone of software design for decades. It organizes code around objects—bundles of data (state) and behavior (methods)—to make systems more modular, maintainable, and scalable.
While OOP principles are consistent, different languages approach them in very different ways. In this article, we’ll explore Java, C++, and Rust—three powerful languages with distinct OOP philosophies.
Core OOP Concepts
We’ll use five core OOP pillars as our comparison framework:
- Encapsulation – Hiding internal state and exposing controlled interfaces.
- Inheritance – Sharing structure/behavior across related types.
- Polymorphism – Treating different types through a common interface.
- Abstraction – Modeling complex systems with simplified representations.
- Memory Management – How object lifetimes and resources are handled.
Encapsulation
Language | How It’s Done | Example Highlights |
---|---|---|
Java | private , protected , public access modifiers; getters/setters are common | Enforced by JVM; true private fields |
C++ | Same modifiers; can be bypassed via pointers; friend keyword | Less strict; more freedom to break encapsulation |
Rust | pub keyword controls module-level visibility; no “class” but struct + impl | Privacy is module-based, no “protected” keyword |
Inheritance
Language | Approach |
---|---|
Java | Single inheritance (extends ) + multiple interfaces |
C++ | Multiple inheritance (can be risky); virtual inheritance to resolve diamond problem |
Rust | No inheritance; favors composition and traits |
Polymorphism (Example)
Java:
javaCopyEditinterface Shape {
void draw();
}
class Circle implements Shape {
public void draw() { System.out.println("Drawing circle"); }
}
Shape s = new Circle();
s.draw();
C++:
cppCopyEdit#include <iostream>
class Shape {
public:
virtual void draw() = 0;
virtual ~Shape() {}
};
class Circle : public Shape {
public:
void draw() override { std::cout << "Drawing circle\n"; }
};
Shape* s = new Circle();
s->draw();
delete s;
Rust:
rustCopyEdittrait Shape {
fn draw(&self);
}
struct Circle;
impl Shape for Circle {
fn draw(&self) { println!("Drawing circle"); }
}
fn draw_shape(s: &dyn Shape) {
s.draw();
}
let c = Circle;
draw_shape(&c);
Abstraction
Language | Mechanism |
---|---|
Java | Abstract classes and interfaces |
C++ | Pure virtual functions and abstract base classes |
Rust | Traits as behavior contracts; no abstract base types |
Memory Management
Language | Memory Model |
---|---|
Java | Garbage collection; objects live on the heap |
C++ | Manual (new/delete ) or smart pointers (std::unique_ptr ) |
Rust | Ownership and borrowing; memory freed automatically at scope end |
Summary Table
Concept | Java (Pure OOP) | C++ (Multi-paradigm) | Rust (Traits + Composition) |
---|---|---|---|
Encapsulation | Strong, enforced | Flexible, can be broken | Module-based |
Inheritance | Single + interfaces | Multiple inheritance | None (composition) |
Polymorphism | Interfaces | Virtual functions | Traits + dyn dispatch |
Abstraction | Interfaces, abstract classes | Abstract base classes | Traits |
Memory Mgmt | GC | Manual or smart ptrs | Ownership model |
Which One Should You Use?
- Choose Java if you want pure OOP, minimal memory concerns, and strong type safety with a large enterprise ecosystem.
- Choose C++ if you need performance-critical systems, fine-grained control over memory, or want to mix paradigms.
- Choose Rust if safety, concurrency, and modern design matter most, and you’re comfortable with composition over inheritance.
In short:
- Java feels familiar and forgiving for OOP beginners.
- C++ gives you ultimate flexibility—at the cost of complexity.
- Rust challenges OOP traditions for the sake of safety and clarity.
Post Comment