Structs em Ruby

Thiago Lima
3 min readSep 22, 2023

O que são Structs?

Você provavelmente já deve ter visto Structs em algumas linguagens e pode estar se perguntando o que são, de onde vêm e para que servem. Structs têm origem em C e são estruturas de dados usadas para agrupar atributos. O Ruby, inspirado nessa ideia, criou sua própria implementação de Struct, que, na minha opinião, é melhor.

No Ruby, um Struct é uma classe que cria novas subclasses nas quais podemos inserir atributos e até métodos. Portanto, o resultado é um objeto. Eu gosto de descrever Structs em Ruby como classes simplificadas.

Como criar um Struct?

É muito simples criar um Struct em Ruby. Vamos criar a estrutura inicial agora:

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

Note que o irb retornou a classe Fruitque foi criada.

Uma forma simplificada de criar Struct é atribuindo ela diretamente a uma constante que será o nome da classe:

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

Dessa forma a classe Fruité criada sem o namespace Struct::, o que, na minha opinião, é mais elegante.

Agora podemos instanciar a classe para atribuir valores a name e price :

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

Vamos verificar o que a variável “banana” retorna:

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

Trata-se de um objeto, então podemos acessar os atributos:

banana.price
=> "5.00"

Também podemos atribuir novos valores:

banana.price = 8.00
banana.price
=> 8.0

Criando métodos no Struct

Vamos tornar as coisas mais interessantes criando Structs com métodos:

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!"

Passando um struct em vez de uma hash

É comum precisarmos passar muitos dados como argumentos ao instanciar uma classe. No entanto, não é uma boa prática passar mais do que 3 argumentos. Veja o exemplo:

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

Isso pode se tornar confuso e difícil de entender. Normalmente, a primeira solução que vem à mente é passar uma hash para resolver:

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

Podemos melhorar:

detail = {
fruit_name: "Banana",
fruit_price: "8.0",
customer: "Marcos",
payment_method: :credit_card
}

BuyFruit.new(detail).call

Ficou bem aceitável, mas eu gostaria de propor algo ainda melhor. Vamos encapsular esses dados em um Struct em vez de uma Hash:

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

BuyFruit.new(detail).call

Indo mais além

Podemos ainda separar os dados para tornar tudo mais semântico:

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

Observe que escrevemos mais código do que simplesmente passar os parâmetros, mas, além de tornar o código mais semântico, você ganha a vantagem de não precisar passar dados avulsos por várias camadas da aplicação. O que você está passando são apenas os dois objetos. A classe BuyFruit não precisa saber o que está contido nesses objetos; ela apenas os repassa para classes mais concretas.

Conclusão

Em resumo, Structs em Ruby oferecem uma maneira poderosa e elegante de criar estruturas de dados e objetos com atributos, métodos e semântica clara. Ao encapsular dados em Structs e utilizá-los de forma eficiente, é possível simplificar o código, torná-lo mais legível e facilitar a passagem de informações entre as camadas de uma aplicação, contribuindo para um desenvolvimento mais organizado e eficaz.

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

--

--

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.