What is a Struct in Ruby?
A struct is a built-in Ruby class, it’s used to create new classes which produce value objects. A value object is used to store related attributes together.
Here’s an example:
A Point
with two coordinates (x
& y
).
You can represent this data in many different ways.
Like:
- An array
[10, 20]
- A hash
{ x: 10, y: 10 }
- An object
Point.new(10, 20)
If you’re going to have more than one Point
, it’s often a good practice to use the object approach.
But…
You don’t want to create a whole class just to store these two values together!
Using a Struct
solves this problem.
Contents
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 accessor methods defined by default, both for reading & writing.
Here’s an example:
Person = Struct.new(:name, :age, :gender)
Now you can create a objects like this:
john = Person.new "john", 30, "M" david = Person.new "david", 25, "M"
Alternative Way
You may find this other way of creating structs in the wild.
It looks like this:
class Person < Struct.new(:name, :age, :gender) end
I don't recommend this.
But if you find it out there now you know what it does.
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".
How to Use Ruby Structs
One of the major benefits from using a struct over an array, or a hash, is that you get to access the struct members using methods.
For example:
puts john.age # 30 puts david.gender # "M"
This is helpful because if you have an array of objects, you can use methods like max
, select
, sum
, etc.
Example:
[john, david].max_by(&:age)
Nice!
WARNING: 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
[responsive_video type='youtube' hide_related='0' hide_logo='0' hide_controls='0' hide_title='0' hide_fullscreen='0' autoplay='0']https://www.youtube.com/watch?v=AlPFIXax3co[/responsive_video]
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.
Now it's your turn to practice 🙂