Struct and OpenStruct

27 Mar 2014 - by Jade

Let’s talk about two Ruby classes that many newer Rubyists may not be aware of.


If you’re familiar with structs in the C programming language, Ruby’s Struct is very similar - a fast and simple way to encapsulate data.

Here’s a simple Ruby class without any defined methods:

class Cat
  attr_accessor :color, :age

  def initialize(color, age)
    @color = color
    @age = age

kuruneko ="black", 16)

And here’s the same class written using Struct:

Cat =, :age)

kuruneko ="black", 16)

As you can guess, attribute readers/writers are implicitly created by Struct definitions. You’ll occasionally see structs written like so:

class Cat <, :age)

If you see that, find the person who wrote it and shout “BAD! BAD PROGRAMMER!” at them until you feel satisfied. returns a new class that interits from Struct, so this adds an unnecessary layer of inheritance. Also, if this gets run twice for whatever reason, you’ll get a scary “superclass mismatch error”. This happens because you just tried to have Cat inherit from two different classes, since creates a unique class object when it executes.

You can even define methods in a struct, though at this point it’s probably better just to use a more traditional class declaration.

Cat =, :age) do
  def meow


OpenStruct is basically a supercharged struct. Unlike Struct, which requires you to define its attributes when it’s created, OpenStruct allows you to arbitrarily create new attributes at will.

require 'ostruct'

cat = = "Mittens"

puts  # => "Mittens"

This actually works just like how you’d imagine - an OpenStruct is just a cheap Hash wrapper, perfect for storing key/value pairs. You can even initialize it by passing in a Hash:

require 'ostruct'
cat = "Mittens")

So, when would you actually use an OpenStruct? OpenStruct plays really well with Ruby’s emphasis on duck-typing. You can think of it as a way to make a dummy object that will respond however you want it to.

In Avdi Grimm’s talk on Confident Coding, he recommends using it as a way of creating default responses for an object that could be nil.

def sayName(args = nil)
  args ||= "My name is", name: "Jade")
  "#{args.greeting} #{}"

sayName # => "My name is Jade"