Mark Chavez

Mark Chavez

Learning how to learn.

The Power of Ruby's Struct

May 25, 2017

I like Structs. They are simple and useful. They provide the same functionalities like classes do. I normally use a Struct if I only need to have accessor methods because it fits the use-case very well.

# Creating a Struct
Person =, :last_name)

person ="Bart", "Simpson")
person.first_name # Bart
person.last_name  # Simpson

It’s simple, concise and elegant. Now, here is a class version of that example.

class Person
  attr_accessor :first_name, :last_name

  def initialize(first_name, last_name)
    @first_name = first_name
    @last_name  = last_name

person ="Kent", "Beck")
person.first_name # Kent
person.last_name  # Beck

Using classes, it took us several lines to actually match our Struct example. While the difference is so trivial, using Struct is still a good margin of improvement and saved us ample time to write.

With Struct, we can also add a method by supplying a block.

Point =, :y) do
  def coordinates
    [x, y]

point =, 2)
point.coordinates # [1, 2]

That’s pretty cool, you say? But is there a really useful way to use Structs? I am pretty sure that there a lot of use-cases that structs are a better option than classes but let me show you one use-case where Struct really shines.

Let us assume that we are trying to call an external API that returns a list of restaurants nearby given our x and y coordinates.

# Assume that this is an external API call to some restaurant service.
restaurants = Net::HTTP.get("api/v1/restaurants/nearby?x=17&y=18").body
first_result = restaurants.first_result

first_result # [ { id: 1, name: "McDonalds" }, { id: 2, name: "Pizza Hut" } ]

As you can imagine, the API returns an array of hash and this is how you are supposed to access the results.

restaurants.each do |restaurant|
  restaurant[:id]   # 1
  restaurant[:name] # McDonalds

At first glance, it seems that the code is written and can be understood easily. But imagine that one restaurant object contains ten(10) additional attributes. That’s where things start to become complicated. In theory, there is really nothing wrong with this except that by using a Struct, this code becomes more idiomatic. Let’s create a Restaurant struct with an id and a name.

Restaurant =, :name)

Now, let’s build a collection of Struct from the external API.

restaurants = Net::HTTP.get("api/v1/restaurants/nearby?x=17&y=18").body
restaurants = do |restaurant|[:id], restaurant[:name])

restaurants # now shows a collection of struct objects representing a restaurant

So if we were to list all restaurants, we can use it like this.

restaurants.each do |restaurant|   # 1 # McDonalds

We could have instantiated the restaurant struct inside the #each method but I prefer not to because with that approach, it is fairly easy to introduce a duplication somewhere else.

Happy reading!

Back to articles