RubyGuides
Share this post!

How to Use Ruby Struct & OpenStruct

A Ruby struct solves one simple problem:

It helps you create a class that is used for storing data & not for defining methods.

Like a Point with two coordinates (x & y).

You wouldn’t want to create a whole class just to store x & y together!

ruby struct

In this post:

You’ll learn how to create structs, the difference between a Ruby Struct & OpenStruct, and a few things you should watch out for!

How to Use Structs in Ruby

A struct is a way to create a new class for the whole purpose of being a data container.

You can create an Struct by passing a list of symbols that’ll become the instance variables of this class.

They will have accessors defined by default, both for reading & writing.

Here’s an example:

person = Struct.new(:name, :age, :gender)

Now you can create new objects of this class with new:

john = person.new "john", 30, "M"

puts john.age
puts john.class

Structs can also be compared directly, based on their attributes:

john == john
# true

If this were a regular Ruby object, then you would have to define the == method yourself.

Structs Can Be Tricky

There are some differences with a “normal” class that you should be aware of.

For example, you may have noticed that the class of our john object is just “Class”…

…to change this, you can do one of the following:

# Option 1 - Assign to a constant
Person = Struct.new(:name, :age, :gender)

# Option 2 - Subclass
class Person < Struct.new(:name, :age, :gender)
end

Both of these options will cause your new objects to have the class name you want.

Another caveat with struct-generated classes... they won't enforce the correct number of arguments for the constructor.

With a proper class you would see this error:

ArgumentError: wrong number of arguments (0 for 3)

But if you are using a Struct the missing arguments will be nil:

Person.new("peter")

# struct Person name="peter", age=nil, gender=nil

Keep this in mind when working with Struct objects!

Another weird thing:

Struct.new(:a).ancestors
[#<Class:0x29b1040>, Struct, Enumerable, Object, Kernel, BasicObject]

#<Class:0x29b1040> is an anonymous class added to structs, also notice the enumerable module, which allows you to call methods like each & map.

Structs in Ruby 2.5

Let's say that you're reading lines from a file & each line represents one item.

Example:

200 /login 18:00
404 /bacon 18:03
200 /books 18:04

To make it easier to work with this data you want to create objects.

You can do it like this:

LogEntry = Struct.new(:status, :url, :time)

LogEntry.new(200, '/books', '18:04')

But to make it extra clear what every argument represents you want to use keyword arguments.

Good news!

Ruby 2.5 added support for keywords arguments in Struct objects.

Here's how to use it:

LogEntry = Struct.new(:status, :url, :time, keyword_init: true)

LogEntry.new(status: 200, url: '/books', time: '18:04')

Now you can parse your file & convert it into LogEntry objects!

How to Use OpenStruct

If you just need a one-off object, then you should consider using OpenStruct instead.

Here's an example:

require 'ostruct'

cat = OpenStruct.new(color: 'black')

puts cat.class
puts cat.color

Warning: OpenStruct is slow and you shouldn't use it on production apps, according to schneems on this reddit comment. Also I found this blog post that has some benchmarks supporting this.

Struct vs OpenStruct:

  • Struct creates a new class with predefined attributes, equality method (==) & enumerable
  • OpenStruct creates a new object with the given attributes

Basically an OpenStruct is a fancy Hash object, while a struct is like creating a new class from a template.

Video Tutorial

Conclusion

You have learned about Ruby Struct & OpenStruct! As long as you are aware of the special characteristics of each of these clases you'll be fine.

Now go and start coding 🙂