In this article, we’re going to explore the following topics:

the Struct class

class structure types and structures

structure type definition behind the scene

Before to start

I’m thrilled to share with you our latest project: Fun Facts about Ruby — Volume 1

Please feel free to spread the word and share this post! 🙏

Thank you for your time!

The Struct class

A structure is a dummy data container. Unlike an object, it’s used for bundling and serving a set of informations without any logic.

It provides a pair of getter/setter methods for each attribute that it contains. This is similar to the attr_accessor method for classes.

Feel free to read the Attributes in Ruby article if you are unfamiliar with the attr_* methods in Ruby.

The Struct class is a structure type builder. This class is in charge of defining new structure types that can generate structures afterwards.

Let’s have a look to its ancestor chain

irb> Struct.class

=> Class

irb> Struct.ancestors

=> [Struct, Enumerable, Object, Kernel, BasicObject]

The Struct class inherits from the default Object class.

It also includes the Enumerable module which is in charge of adding a bunch of searching, sorting and traversal methods to a class.

This class shares the exact same ancestor chain as the Array and Hash classes.

Feel free to read the Ruby Object Model article if you are unfamiliar with the Object class and the ancestor chain. Feel free to read the The Enumerable module in Ruby: Part I article if you are unfamiliar with the Enumerable module.

Structure types and structures

A structure type is a blueprint (class) that contains an immutable list of attributes — also called members.

A structure is the representation in memory of this blueprint (object).

Now let’s see how to create a structure type.

The Struct::new method is a structure type builder. It allows you to define a new structure type associated to a bunch of defined members passed as parameters

In the first line, we define the Address structure type which contains the street , city and zip members.

Then we instantiate a structure which is of Address type that we store in the home variable.

Each argument of Address.new(‘Broadway’, ‘NYC’, 10002) matches the corresponding argument of the Struct.new(:street, :city, :zip) in the given order.

Here, we can see that there is 3 ways to access the value of a member:

home.street : the street accessor method

: the accessor method home[:city] : the Struct#[] with a symbol key

: the with a key home['zip'] : the Struct#[] with a string key

Also, notice that if you try to access a non-existing member then a NoMethodError or a NameError is raised depending on the way to access this member.

It’s also possible to modify the value of a member for a given structure

Here, we can see that there is 3 ways to modify the value of a member:

home.street= : the street= accessor method

: the accessor method home[:city] : the Struct#[]= with a symbol key

: the with a key home['zip'] : the Struct#[]= with a string key

Also notice that if you try to modify a non-existing member then a NoMethodError or a NameError is raised depending on the way to modify this member.

Now that we are more familiar with structures and structure types, let’s dig into how structure types are defined behind the scene.

Structure type definition behind the scene

Like any class in Ruby, the Struct::new method should instantiate an object of type Struct .

But what if I tell you that the Struct class is not instantiable ?

irb> Struct.allocate

TypeError (allocator undefined for Struct)

irb> Struct.methods(false)

=> [:new]

In effect, the Struct#allocate method — in charge of allocating the memory space needed to contain a Struct object — is undefined at the Struct class definition.

So, the Struct class cannot allocate the needed memory to instantiate an object of type Struct .

Also, the Struct class overrides the BasicObject#new method by implementing its own version.

So, how structure types are defined if we cannot instantiate a Struct ?

Behind the scene, Ruby makes a little bit of magic to give the illusion that this class is instantiable.

And all this magic is defined in the Struct#new method.

If effect, this method doesn’t instantiate a Struct but, instead, creates a subclass of its own

Here, the Address constant is actually a Class that inherits from the Struct class.

This allows the Address class to have access to all of the methods and internals of the Struct class.

So, a structure type is in reality a named class that inherits from the Struct class and a structure is simply an instance of this named class.

This powerful design allows our structure type to enjoy all the mechanisms that a class provides in Ruby such as class opening, inheritance, mixins etc...

For example, we can re-open the Address class to add a full_address method

This is the basic use of the Struct::new method.

Otherwise, there is another way to use this method

By passing the structure type name as first argument of the Struct::new , the method automatically defines a new class under the scope of the Struct class which also inherits from Struct .

Then we can instantiate the freshly defined Struct::Address structure type and store the structure in the home variable.

Voilà !

ONE MORE THING ⬇

Feel free to subscribe here: www.rubycademy.com