Rust Adventures — From Java Class to Rust Struct

Hi there folks!

As you already know I´m learning Rust as one of my goals to 2020. The major of my experience was with Java up until now, although I played with some others. So the Object-Oriented Programming is part of my daily life.

Classes are in the core of Java, there is simply no way to write a Java program without a class, even to print a Hello World you will need a class.

source: https://www.learnjavaonline.org/en/Hello%2C_World%21

But this concept does not exists in Rust, but some concepts of it can be for sure from OOP, but how?

Structs

Rust has a great variety of data structures, one of them is tuples that is a collection of values that can be accessed by their positions. Here is an example:

This simple program creates a tuple and print its contents. As you can see, we can attach different data types inside a tuple and access it by its current position. The result of running the program will be the following:

Tuples can be used for simple operations or when the meaning of each position is well known, otherwise it can be very difficult to maintain and develop further improvements.

Struct is similar to a tuple, but it needs a name to the data structure and a name for its fields. So we can change our tuple to a struct:

We defined a struct of name Example using the reserved word struct followed by {} and inside you put the fields that consists of the pattern name:value_type. Inside the main method we wanted to create a variable complex_data of type Example.

A Java programmer could see some similarities between a struct and a class, but the first thing you could notice is the lack of a constructor to create the complex_data. In rust we do not create objects itself, we call them instances. To create a instance you just use the struct name and a pair of {}, inside you put the name of the fields with values.

Differently from the tuple we don’t need to follow any order of the fields, we can construct the instance with any order and access the values just with the names.

One of the concepts of OOP is that the objects contains data in form o fields, although there are no classes or objects Rust structs holds data in form of fields, so we could say that the principle is still respected. The other responsibility of an object is to hold the behavior in forms of procedures or methods, in Rust we can make it with implementations.

Implementations

In Rust the structure syntax doesn’t support methods, for this we use the impl keyword, where you can have two kinds of procedures: methods and associated functions.

Functions we have already seen, they live outside an struct like the main function and needs the fn keyword.

The methods are functions that exists inside the struct implementation and receives the &self parameter, indicating that it needs to be called by an instance with a “.”.

In the code above we implemented the Example struct and gave it two methods, note that the &self parameter references the instance itself, and you don’t need to pass it when calling the method.

The associated methods are called without an instance, it can be compared with the Java static functions. They are normally are used as a constructor of instances.

The new associated function creates for us an instance of Example, as you can see the syntax to call this function is with “::”. We can now work with some different ways of creating instances in rust, so let’s create some others associated functions:

There are some sugar syntaxes in struct instances creation, the first is with a parameter or variable with the same name of a struct field you can pass it directly and Rust will understand, like “with_a_integer”. Second, you can use another instance with “..” and copy the not specified fields. Those tricks make easier to create instances. To test let’s modify our main function:

Now we have implemented the second principle of OOP objects, code in form of procedures, in this case methods and associated functions.

Inheritance

But what about inheritance? Does rust supports such feature?

Well, not as you would think. Over the years this concept started to falling out of favor because of complexity that a tree of classes can get and how it exposes their fields and methods.

But we can get something similar with Traits for inheritance and generics for parametric polymorphism.

Traits

Traits works similar to Interfaces in Java, they define a common behavior that some structs might implement, and with them we can create traits objects. A trait object looks for a type that implements the trait as well to a table with methods that it can call.

The trait syntax is very similar to a impl block, but instead of having methods or associated functions with a body they finish with “;”.

Although it’s called trait object, we cannot compare it a normal object, it combines data and procedures, but you cannot change the data directly, it must have an implementation on a struct for that, because of its primary purpose of implementing a common behavior, like a contract, in sense it really is more like a Java Interface than an object.

The basic use is to just implement the trait to a struct, like we would make a default implementation of it.

Defined the Printable trait we made the method print, any struct that implement it will have to create the implementation of this method. So we can have various implementations for one struct.

It’s really different from Java where the class encapsulates all the logic and data, we can now have different parts of the same struct being implemented in different parts of a program, a consequence of that is you can define different behaviors just importing the traits, because the moment the scope reads the import will put that piece of implementation over you instance.

Another way to use traits is to pass it as a type of a struct field, that gives us some behavior close to polimorfism. There are two different ways to do that:

Another way to make it works is to define Printable trait as a generic, we can do this with the where clause. But now we must encapsulate the generic with a parameter:

But it would be complicated to work if you want default values, because rust needs to have this generic constrained inside some pointer like the box example, if it were a Vec for example it’s ok as well. So the first form is the best notation for a generic type field.

We can define a trait as a generic in a fn syntax as well, it’s similar for a Java counterpart.

Although we can’t have the tradition Java inheritance we can define and pass common behavior with Traits making more OOP programs.

So we can do a Strategy with that?

Strategy is a behavioral design pattern that enable you to select an algorithm in runtime. It consists of a common of some kind of interface if a common behavior and is normally used to reduce conditional complexity of the systems.

The layout is pretty simple:

We have an Interface that is our Strategy, you can name it with a Strategy suffix or something similar, and them their implementation where the code resides. Any operation that needs to use the strategy will call the interface, not the implementation, with that we can make a cleaner and secure code.

Let’s say we want to calculate different types of fractions of two numbers.

The Calculator struct has a “method calculate” that receives two numbers and compare its field mixed_numbers to decide how to calculate the fraction of both numbers.

This simple example does not show the dangerous of a conditional complexity, but if this code was meant to grow we could have some difficult to maintain and develop new formulas.

So Let’s apply the the pattern, first we create a trait with a method calculate_fraction that will be our strategy.

Now the struct Calculator will not carry anymore a bool field but a Box of FractionStrategy.

The “method calculate” now don’t need to decide how to calculate, it just do it, and the instance of FractionStrategy will be the one responsible of how to calculate it. So let’s create two implementations of it, each one with its own way to calculate fraction.

Now we can use them to instance the Calculator struct and perform in runtime the change in calculation.

We could even change the type of FractionStartegy in the middle of the program. If calculator1 is defined as mut we could change the fraction_strategy field at runtime.

Conclusions

For sure Rust is a unique language. Its design for secure memory and performance requested some originals approaches of common programming and software design problems.

Although we can see some similarities to Java, Rust demands some effort to understand and think software by its rules, but the benefit is great when we can make a fast and reliable program as robust as Java is.

It’s not only OOP principles, Rust implemented some functional paradigm as well, but it’ll come in a future article.

I hope you enjoyed this article, until next time.

Analytics Vidhya

Analytics Vidhya is a community of Analytics and Data…

Thanks to Henrique Peixoto Machado

Floriano Victor Peixoto

Written by

Writing to make ourselves better programmers.

Analytics Vidhya

Analytics Vidhya is a community of Analytics and Data Science professionals. We are building the next-gen data science ecosystem https://www.analyticsvidhya.com

Floriano Victor Peixoto

Written by

Writing to make ourselves better programmers.

Analytics Vidhya

Analytics Vidhya is a community of Analytics and Data Science professionals. We are building the next-gen data science ecosystem https://www.analyticsvidhya.com

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store