Structs in Ruby

Thiago Lima
3 min readSep 22, 2023

--

What are Structs?

You’ve probably seen Structs in some languages and may be wondering what they are, where they come from, and what they’re used for. Structs originate from C and are data structures used to group attributes. Ruby, inspired by this concept, created its own implementation of Struct, which, in my opinion, is superior.

In Ruby, a Struct is a class that creates new subclasses in which we can insert attributes and even methods. Therefore, the result is an object. I like to describe Structs in Ruby as simplified classes.

Creating a Struct

Creating a Struct in Ruby is very simple. Let’s create the initial structure now:

Struct.new("Fruits", :name, :price)
=> Struct::Fruits

Note that irb returned the Fruit class that was created.

A simplified way to create a Struct is to assign it directly to a constant that will be the class name:asse:

Fruits = Struct.new(:name, :price)

This way, the Fruit class is created without the Struct:: namespace, which, in my opinion, is more elegant.

Now we can instantiate the class to assign values to name and price:

banana = Fruits.new("Banana", "5.00")

Let’s check what the “banana” variable returns:

=> #<struct Fruits name="Banana", price="5.00">

It’s an object, so we can access the attributes:

banana.price
=> "5.00"

We can also assign new values:

banana.price = 8.00
banana.price
=> 8.0

Creating Methods in a Struct

Let’s make things more interesting by creating Structs with methods:

Fruit = Struct.new(:name, :price) do
def announce
"Buy #{name} for just $#{price}!"
end
end
banana = Fruit.new("banana", "5.00")
banana.announce
=> "Buy banana for just $5.00!"

Passing a Struct Instead of a Hash

It’s common to need to pass many data arguments when instantiating a class. However, it’s not a good practice to pass more than 3 arguments. See the example:

BuyFruit.new("Banana", "8.0", "Marcos", :credit_card).call

This can become confusing and hard to understand. Typically, the first solution that comes to mind is to pass a hash to resolve it:

BuyFruit.new(fruit_name: "Banana", fruit_price: "8.0", customer: "Marcos", payment_method: :credit_card).call

We can improve this:

detail = {
fruit_name: "Banana",
fruit_price: "8.0",
customer: "Marcos",
payment_method: :credit_card
}
BuyFruit.new(detail).call

It’s acceptable, but I’d like to propose something even better. Let’s encapsulate this data in a Struct instead of a Hash:

Details = Struct.new(:fruit_name, :fruit_price, :customer, :payment_method)
detail = Details.new("Banana", "8.0", "Marcos", :credit_card)
BuyFruit.new(detail).call

Going Further

We can further separate the data to make it more semantic:

class BuyFruit
def initialize(fruit, customer)
@fruit = fruit
@customer = customer
end

def call
StockRemove.new(@fruit).call
PaymentGateway.new(@fruit, @customer).call
end
end
Fruits = Struct.new(:name, :price, keyword_init: true)
Customers = Struct.new(:name, :payment_method, keyword_init: true)
fruit = Fruits.new(name: "Banana", price: "8.0")
customer = Customers.new(name: "Marcos", payment_method: :credit_card)
BuyFruit.new(fruit, customer).call

Note that we’ve written more code than simply passing the parameters. However, besides making the code more semantic, you gain the advantage of not having to pass individual data through multiple layers of the application. What you’re passing are just two objects. The BuyFruit class doesn’t need to know what’s inside these objects; it simply passes them to more concrete classes.

In Conclusion

In summary, Structs in Ruby offer a powerful and elegant way to create data structures and objects with attributes, methods, and clear semantics. By encapsulating data in Structs and using them efficiently, you can simplify your code, make it more readable, and facilitate the passing of information between layers of an application, contributing to more organized and effective development.

ref.: https://ruby-doc.org/core-2.5.1/Struct.html

--

--

Thiago Lima
Thiago Lima

Written by Thiago Lima

Hello! I’m Thiago Lima, I’m maried, I have a son named Isaac. I’m software engineer and I programming in Ruby on Rails.