Intro

Ruby has a lot of useful data structures. In this blog, we’ll explore Struct and OpenStruct. We’ll see how each is used, and learn when to use one or the other.

If you just want to see when to use one or the other, skip to the Summary.

Struct

Let’s define it:

Ruby’s official documentation says:

a convenient way to bundle a number of attributes together, using accessor methods, without having to write an explicit class.”

How is it used?

Struct is essentially a shortcut for building simple classes with attributes and maybe a method or two. Let’s see how it’s used:

User = Struct.new(:first, :last) do
  def email
    "#{first}.#{last}@mrod.space"
  end
end

mike = User.new("Michael", "Rodrigues")

mike.first
#=> "Michael"

mike.last
#=> "Rodrigues"

mike.email
#=> "[email protected]"

mike.class
#=> User

If the example looks familiar, I basically ripped it right out of the Ruby core documentation.

Make a mental note of this: Struct is part of Ruby core. Its source code is written in C. This is in contrast to OpenStruct which is part of the standard library. It’s written in Ruby and you have to require it before you use it. You don’t need to do anything to use Struct.

OpenStruct

Let’s define it:

The official Ruby documentation says:

a data structure, similar to a Hash, that allows the definition of arbitrary attributes with their accompanying values. This is accomplished by using Ruby’s metaprogramming to define methods on the class itself.

How is it used?

Unlike Struct, OpenStruct isn’t going to define a new class to instantiate objects from. However, it will let you arbitrarily define new attributes on the fly. Let’s look a tweaked example from the Ruby documentation:

require "ostruct" # remember, part of std lib, not core

event = OpenStruct.new
event.timestamp = Time.now
event.message = "Something notable happened."

event.timestamp
#=> 2019-08-29 23:20:29 -0700

event.message
#=> "Something Notable happened."

event.sender
#=> nil

event.sender = "Michael"
event.sender
#=> "Michael"

We instantiate our OpenStruct object, event, and instantly set two attributes on it. Notice we didn’t define them beforehand, we just set them at will. We can easily get them by name.

You can also pass in a Hash at instantiation:

require "ostruct"

event = OpenStruct.new(
			{
			  timestamp: Time.now,
			  message: "Short message.",
			  sender: "Michael"
		        }
)

event.sender
#=> "Michael"

Just toss in the Hash and everything is automatically instantiated based on the Hash’s keys.

Summary

Comparison

  • How they’re instantiated:
    • Struct is instantiated with a class name, list of attributes, and optional methods
  • What instantiation returns:
    • Struct.new returns a class with fixed attributes
    • OpenStruct.new returns an instance of OpenStruct, attributes can be added and removed after instantiation
  • Where they’re from:
    • Struct is part of Ruby core, written in C
    • OpenStruct is part of the Ruby Standard Library, written in Ruby
  • Flexibility vs. Performance
    • Struct sacrifices flexibility for performance
    • OpenStruct sacrifices some performance for flexibility and ease of use

When to Use

Use Struct when:

  • speed is a concern
  • you know all of your attributes at instantiation

Use OpenStruct when:

  • speed is not a concern
  • you don’t know all of your attributes at instantiation