Characteristics of Object-Oriented Programming

How Rust approaches the three core object-oriented concepts — objects, encapsulation, and inheritance — and why the language takes a different path from classical OOP

Object-oriented programming is one of those terms that means different things to different people. Some definitions emphasize message-passing between objects. Others focus on inheritance hierarchies. The Gang of Four book, one of the most influential works on OOP design, defines it simply: a program built from objects that package data together with the procedures that operate on that data.

By that definition, Rust is object-oriented. Structs and enums hold data, impl blocks attach behavior to that data, and the resulting units function like objects in any classical OOP language. But Rust deliberately omits inheritance — a feature many programmers consider essential to OOP — and replaces it with a combination of traits, generics, and composition.

This section examines the three characteristics most commonly associated with object-oriented languages and maps each one onto Rust's actual mechanisms. You will see where Rust aligns with traditional OOP, where it diverges, and what practical trade-offs those divergences create.

The Three Core OOP Characteristics

When programmers debate whether a language qualifies as object-oriented, the conversation usually revolves around three features. Not every OOP language has all three, and not every language that has them is purely object-oriented. But together they form a useful lens for understanding Rust's design.

Objects — the idea that a program's building blocks bundle data with the code that manipulates that data. In classical languages, these are instances of classes. In Rust, they are instances of structs or enums with impl blocks.

Encapsulation — the practice of hiding implementation details behind a public interface, so that code using an object cannot reach into its internals and depend on details that might change. Rust enforces this with its module system and the pub keyword.

Inheritance — a mechanism where one type automatically acquires the fields and methods of a parent type. This is where Rust departs most visibly from classical OOP. Rust has no struct inheritance whatsoever. Instead, it uses traits for shared behavior and composition for code reuse.

No Single Definition:

The programming community has never agreed on a single definition of object-oriented programming. Alan Kay, who coined the term, emphasized message-passing. The Gang of Four emphasized data-behavior bundling. Java and C++ emphasize inheritance. Rust draws from all these traditions but commits to none of them exclusively.

Rust's Multi-Paradigm Approach

Rust is not a purely object-oriented language, nor is it a purely functional one. It borrows ideas from multiple paradigms and combines them into a single coherent system. You can write Rust in an object-oriented style, structuring your code around structs with methods and private fields. You can also write it in a functional style, leaning on closures, iterators, and pattern matching. Most idiomatic Rust code blends the two.

This flexibility matters because it means Rust does not force you into one way of thinking. When an OOP pattern fits the problem — bundling state with behavior, hiding implementation details — Rust gives you the tools to do it cleanly. When inheritance would be the natural OOP solution, Rust asks you to reach for a trait or a composition instead, and those alternatives often produce more maintainable code in the long run.

What This Means in Practice:

If you come to Rust from Java, C#, or C++, you will recognize the patterns: private fields, public methods, constructors, and polymorphic interfaces. The syntax differs, and some familiar features are missing, but the design thinking transfers. Rust does not reject OOP — it refines it by removing the parts that decades of industry experience have shown to be problematic.

How the Subsections Are Organized

The three subsections that follow correspond directly to the three characteristics.

The first, Objects Contain Data and Behavior, starts from the Gang of Four definition and shows how Rust's structs, enums, and impl blocks fulfill it. If you have ever written a class in another language, this subsection maps that concept onto Rust's equivalent.

The second, Encapsulation with Modules and Visibility, explains how Rust keeps internal details hidden. Unlike languages that attach visibility modifiers to individual fields, Rust controls privacy at the module boundary. Understanding this difference is essential for structuring larger Rust projects.

The third, Inheritance and Polymorphism via Traits, tackles the most significant departure from classical OOP. Rust has no class inheritance, but it achieves code reuse through default trait methods and achieves polymorphism through generics with trait bounds and through trait objects. This subsection explains both mechanisms and when to use each.

Expect Conceptual Differences:

If your mental model of OOP is built entirely on class hierarchies, Rust's approach to inheritance will feel unfamiliar at first. The language does not simply rename classes to structs — it replaces a hierarchical model of code organization with a compositional one. This is a deliberate design choice, not an oversight.