When should I use Struct vs. OpenStruct?
With an OpenStruct
, you can arbitrarily create attributes. A Struct
, on the other hand, must have its attributes defined when you create it. The choice of one over the other should be based primarily on whether you need to be able to add attributes later.
The way to think about them is as the middle ground of the spectrum between Hashes on one side and classes on the other. They imply a more concrete relationship amongst the data than does a Hash
, but they don't have the instance methods as would a class. A bunch of options for a function, for example, make sense in a hash; they're only loosely related. A name, email, and phone number needed by a function could be packaged together in a Struct
or OpenStruct
. If that name, email, and phone number needed methods to provide the name in both "First Last" and "Last, First" formats, then you should create a class to handle it.
When is it better to use a Struct rather than a Hash in Ruby?
Personally I use a struct in cases when I want to make a piece of data act like a collection of data instead of loosely coupled under a Hash
.
For instance I've made a script that downloads videos from Youtube and in there I've a struct to represent a Video and to test whether all data is in place:
Video = Struct.new(:title, :video_id, :id) do
def to_s
"http://youtube.com/get_video.php?t=#{id}&video_id=#{video_id}&fmt=18"
end
def empty?
@title.nil? and @video_id.nil? and @id.nil?
end
end
Later on in my code I've a loop that goes through all rows in the videos source HTML-page until empty?
doesn't return true.
Another example I've seen is James Edward Gray IIs configuration class which uses OpenStruct
to easily add configuration variables loaded from an external file:
#!/usr/bin/env ruby -wKU
require "ostruct"
module Config
module_function
def load_config_file(path)
eval <<-END_CONFIG
config = OpenStruct.new
#{File.read(path)}
config
END_CONFIG
end
end
# configuration_file.rb
config.db = File.join(ENV['HOME'], '.cool-program.db')
config.user = ENV['USER']
# Usage:
Config = Config.load_config('configuration_file.rb')
Config.db # => /home/ba/.cool-program.db
Config.user # => ba
Config.non_existant # => Nil
The difference between Struct
and OpenStruct
is that Struct
only responds to the attributes that you've set, OpenStruct
responds to any attribute set - but those with no value set will return Nil
When should I use an OpenStruct instead of a Hash?
I think this mostly comes down to a performance decision. From the Ruby Documentation:
An OpenStruct utilizes Ruby’s method lookup structure to and find and define the necessary methods for properties. This is accomplished through the method method_missing and define_method.
This should be a consideration if there is a concern about the performance of the objects that are created, as there is much more overhead in the setting of these properties compared to using a Hash or a Struct.
Additionally, something like a Hash
has additional functionality with all of the methods it provides (has_key?
, include?
, etc.). The OpenStruct
is a very simple object from that standpoint, but if you don't have any concerns from a performance standpoint and just want an easy object to work with, OpenStruct
is a good choice.
Whats are some important differences between an OpenStruct and a Hash?
OpenStructs are sloooooooooow and memory intensive , and don't scale well for large data sets.
Creating 1 million OpenStructs is ~100x slower than creating 1 million Hashes.
This has been discussed in detail here:
When should I use Struct vs. OpenStruct?
Is it required to place struct in front of a struct instance in c++ when you use c header files?
Even if you use all the C
stuff (headers) in your program, at the end of the day it is recognized as a C++
source by your system if the file ends with .cpp
extension. So, if you don't need to put struct in front of every struct
instance as long as your source file is a C++
source file, it shouldn't show any compilation error.
That implies NO as the answer to your question.
Edit: As @JesperJuhl pointed out, this is not always true. Exceptions happen on AiX, VMS, Novell NetWare, DOS system. In that case, you'll need to use struct
keyword before every instance.
What's the purpose of an anonymous struct in Ruby?
Struct.new
returns a Class
, so you can, for example, assign it to a constant like this:
Point = Struct.new(:x, :y)
or subclass it:
class Point < Struct.new(:x, :y)
# custom methods here
# ...
end
In both cases, you can use the resulting class like this:
Point.new(3, 5)
If you don't want to create a specific class (because you need to instantiate an object of that class only once), consider to use OpenStruct
instead:
require 'ostruct'
point = OpenStruct.new(:x => 3, :y => 5)
Construct nested OpenStruct object
This method is rude method but works,
require 'ostruct'
require 'json'
# Data in hash
data = {"names" => {"first_name" => "Bob"}}
result = JSON.parse(data.to_json, object_class: OpenStruct)
And another method is adding method to Hash class itself,
class Hash
def to_openstruct
JSON.parse to_json, object_class: OpenStruct
end
end
Using above method you can convert your hash to openstruct
data = {"names" => {"first_name" => "Bob"}}
data.to_openstruct
Ruby Class vs Struct
From the Struct docs:
A Struct is a convenient way to bundle a number of attributes together, using accessor methods, without having to write an explicit class.
The Struct class generates new subclasses that hold a set of members and their values. For each member a reader and writer method is created similar to Module#attr_accessor.
So, if I want a Person
class that I can access a name attribute (read and write), I either do it by declaring a class:
class Person
attr_accessor :name
def initalize(name)
@name = name
end
end
or using Struct:
Person = Struct.new(:name)
In both cases I can run the following code:
person = Person.new
person.name = "Name"
#or Person.new("Name")
puts person.name
When use it?
As the description states we use Structs when we need a group of accessible attributes without having to write an explicit class.
For example I want a point variable to hold X and Y values:
point = Struct.new(:x, :y).new(20,30)
point.x #=> 20
Some more examples:
- http://blog.steveklabnik.com/posts/2012-09-01-random-ruby-tricks--struct-new
- "When to use Struct instead of Hash in Ruby?" also has some very good points (comparing to the use of hash).
Related Topics
Understanding Private Methods in Ruby
When Should I Use Struct Vs. Openstruct
Rails 3, Has_One/Has_Many With Lambda Condition
Why Isn't the Eigenclass Equivalent to Self.Class, When It Looks So Similar
Sqlite3-Ruby Install Error on Ubuntu
Create Two-Dimensional Arrays and Access Sub-Arrays in Ruby
How to Solve "Ruby Installation Is Missing Psych" Error
Does Ruby Perform Tail Call Optimization
Why Is This Rmagick Call Generating a Segmentation Fault
Rails Model Has_Many With Multiple Foreign_Keys
"Bin/Rails: No Such File or Directory" W/ Ruby 2 & Rails 4 on Heroku
What Tools Do You Recommend to Profile Rails Apps
I Don't Understand Ruby Local Scope
Heroku - Cannot Run Git Push Heroku Master