Ruby Class vs Struct

To add to the other answers, there are some things you can not do with a Struct, and some than you can.

For example, you can not create a Struct with no arguments:

Bar = Struct.new
=> ArgumentError: wrong number of arguments (given 0, expected 1+)

Bar = Struct.new(:bar)
bar = Bar.new(nil)
bar.class
=> Bar

However, a class will let you do that:

class Foo; end
foo = Foo.new
foo.class
=> Foo

You can not set a default value for Struct arguments:

Bar = Struct.new(bar: 'default')
=> ArgumentError: unknown keyword: bar

Bar = Struct.new(bar = 'default')
=> NameError: identifier default needs to be constant

But you can do it with a class, either passing a hash, were the arguments can be in any order or even missing:

class Bar
  attr_reader :bar, :rab
  def initialize(bar: 'default', rab:)
    @bar = bar
    @rab = rab
  end
end

bar = Bar.new(rab: 'mandatory')
bar.rab
=> 'mandatory'
bar.bar
=> 'default'

bar = Bar.new(rab: 'mandatory', bar: 'custom_value')
bar.rab
=> 'mandatory'
bar.bar
=> 'custom_value'

or passing the values directly, were the arguments should be given in the same order, with the defaulted ones always at the end:

class Bar
  attr_reader :rab, :bar
  def initialize(rab, bar = 'default')
    @rab = rab
    @bar = bar
  end
end

bar = Bar.new('mandatory')
bar.rab
=> 'mandatory'
bar.bar
=> 'default'

bar = Bar.new('mandatory', 'custom_value')
bar.rab
=> 'mandatory'
bar.bar
=> 'custom_value'

You can not do any of that with Structs, unless you set default values for your arguments in this super verbose way:

A = Struct.new(:a, :b, :c) do
  def initialize(a:, b: 2, c: 3)
    super(a, b, c)
  end
end

(example taken from this answer)

You can define methods in a Struct:

Foo = Struct.new(:foo) do
  def method(argument)
    # do something with argument
    end
  end
end

Structs can be useful to create data objects, like the point example mentioned in one of the answers.

I sometimes use them to create fakes and mocks in tests in a simple way. Sometimes RSpec allow(foo).to receive(:blah) etc. can get a bit too verbose and using a Struct is much simple.


Struct is a Ruby shorthand for creating Classes. Using Struct where applicable simplifies your code. There is a good discussion of this at https://www.rubytapas.com/2012/11/07/episode-020-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).

Tags:

Ruby