RubyGuides
Share this post!

How to Use Struct & OpenStruct in Ruby

What is a Struct in Ruby?

Let’s say that you need to store related data together but you don’t need methods & you don’t want to create a class…

A Ruby Struct solves this problem by letting you create objects with simple syntax.

Here’s an example:

A Point with two coordinates (x & y).

You wouldn’t want to create a whole class just to store these two values together, would you?

In this post, you’ll discover:

  • How to create structs
  • The difference between a Ruby Struct & OpenStruct
  • Things you should watch out for

Let’s do this!

How to Create Structs in Ruby

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

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

Here’s an example:

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

Now you can create an object like this:

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

puts john.age
puts john.class

Big Benefit

A good thing about structs if that you can compare them directly, based on their attributes.

Example:

john == john
# true

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

This is what we call a “value object“.

Using Ruby Structs

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.

If you want to give your struct a name…

You should use one of these:

# 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 make your new objects have the class name you want.

Which is good!

Option 1 is the most common.

Structs Can Be Tricky

Another caveat with struct-generated classes is that...

They won't enforce the correct number of arguments for the constructor!

Let me explain.

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

Take a look at this...

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

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

Named Parameters 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

You work more easily with this data if you create a custom class.

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 may 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.

Code example:

require 'ostruct'

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

puts cat.class
puts cat.color

Notice how you have to require ostruct to have access to this class.

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

The difference between Struct & OpenStruct:

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

An OpenStruct is a fancy Hash object, while a Struct is like creating a new class from a template.

Watch Video Tutorial

Conclusion

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

Ruby Struct Mindmap

Now it's your turn to practice 🙂