Object-oriented programming is a programming paradigm that revolves around the concept of objects which are instances of classes. These objects can contain the data and behavior which make organizing and managing the complex codebases easier.
While Rust is primarily known for its focus on low-level systems programming and strong safety features, it also supports object-oriented programming concepts.
This tutorial demonstrates how to use the native Rust features to build an object-oriented program.
Point of Notice
While Rust provides the OOP-like features, it is not traditionally considered as a native object-oriented programming (OOP) language like Java or C++. Instead, it primarily focuses on systems programming, memory safety, and concurrency. Hence, you will find it leaning towards ownership, borrowing, lifetimes, and more to ensure the memory safety without needing a garbage collector.
That being said, Rust provides powerful abstractions and features such as structs, enums, and traits which we can use to achieve the object-oriented-like behavior.
Create a New Project
The first step is to set up a basic Rust project which allows us to demonstrate the basics of OOP in Rust.
Hence, run the cargo command as follows:
This creates a new project in the current working directory under the “oop_rust” name. Feel free to rename this directory to your desired name.
Once created, open the project in your editor such as Visual Studio Code or Xcode.
Step 1: Define the Structs in Rust
Unlike native OOP languages such as C++ and Java, we present the objects as structs in Rust. This allows us to define a given object and its various properties.
Let us see how we can define a simple struct to represent a “Rectangle” object with two attributes.
Create a new file called “rectangle.rs” in the src directory of your project. Then, proceed to cover how to define an object as a struct.
The first step is to define the struct using the “struct” keyword followed by the name of the struct.
width: i32,
height: i32,
}
In this case, we define a “Rectangle” struct with two fields: width and height, both of type i32 (32-bit signed integer).
The “pub” keyword makes the struct and its fields public which allows us to access them from outside the module in which they are defined.
The next step is to define an implementation block:
// methods
}
The implementation block contains the implementation of methods that are associated with the “Rectangle” struct.
We can define a method inside the impl block as shown in the following:
Rectangle {width, height}
}
In this case, we define a new method that acts as a constructor for the “Rectangle” struct. It takes two parameters: width and height, both of type i32.
The -> self part indicates that this method returns an instance of self (which refers to Rectangle in this case).
The method creates a new Rectangle instance using the input parameters and initializes the width and height fields. Finally, it returns the newly created Rectangle instance.
We can also define another method inside the impl block. In this case, let us define a method called “area”.
self.width * self.height
}
As the name suggests, the method calculates the area of the rectangle. It takes a reference to self (the current instance of Rectangle) as the first parameter using the “&self” syntax.
The method returns an i32 data type as shown in the block -> i32. The method returns the result implicitly, as we did not define an explicit return statement.
The full source code is as follows:
width: i32,
height: i32,
}
impl Rectangle {
pub fn new(width: i32, height: i32) -> Self {
Rectangle { width, height }
}
pub fn area(&self) -> i32 {
self.width * self.height
}
}
Step 2: Create an Object and Use the Method
Once we have our “Rectangle” struct, we can create an object from it and access the “defind” methods.
Add the following code in the “main.rs file” as shown in the following:
use rectangle::Rectangle;
fn main() {
let rectangle = Rectangle::new(4, 8);
println!("Rectangle area: {}", rectangle.area());
}
In the previous code, we start by importing the “Rectangle” struct from the rectangle module. Then, we create a new Rectangle object with a width of 4 and a height of 8.
Finally, we call the “area” method on the rectangle object and print the result.
We can now compile the code with the following command:
This should compile and run the binary. The resulting output is as follows:
You can now have a fundamental way of creating the Rectangle objects from the defined struct and access the provided attributes and methods. You can also add more functionality to the implementation which allows you to encapsulate more data and behavior inside the struct.
Conclusion
This tutorial explored an example of implementing the object-oriented programming in Rust. We defined a “Rectangle” struct with attributes and methods, created the objects from the struct, and utilized the object’s methods.