Hidden feature in Ruby’s Struct

This article originally appeared upon on texperts.com

Ruby LogoThe Ruby core library contains a nice little utility class called Struct, which provides a convenient way to bundle a number of attributes together, using accessor methods, without having to write an explicit class. So this:

class Customer
  attr_accessor :name
  attr_accessor :address
 
  def initialize(name, address)
    @name = name
    @address = address
  end
end

is (broadly) equivalent to this:

Customer = Struct.new(:name, :address)

Much nicer (and DRYer)!

But what if you want to define methods on the new class that you’ve just created? The Pickaxe Book says:

Ruby 1.9 and later allow you to pass a block to a Struct’s constructor. This block is evaluated in the context of the new struct’s class and hence allows you conveniently to add instance methods to the new struct.

Unfortunately most of us haven’t (yet) moved to Ruby 1.9, but there is good news! It turns out that this functionality has actually been present ever since 1.8.3 (here’s the relevant ChangeLog entry, although note that when it refers to [ruby-talk:02606], it should really refer to [ruby-core:02606]).

Imagine, for example, that we wanted to add a custom to_s method to our Customer class:

Customer = Struct.new(:name, :address) do
  def to_s
    "A customer called '#{name}' living at '#{address}'"
  end
end

Sweet!

Update (2007-09-05)

It turns out that there is another commonly used idiom which achieves much the same effect:

class Customer < Struct.new(:name, :address)
  def to_s
    "A customer called '#{name}' living at '#{address}'"
  end
end

However, it also turns out that both of these idioms may have problems when combined with Rails. For further information see this discussion on the ruby-talk mailing list.

1 Response to “Hidden feature in Ruby’s Struct”


  1. 1 John Prince

    Very helpful! I’ve used a sort of class access like the last one shown:

    Customer = Struct.new(:name, :address)

    class Customer
    def to_s

    end
    end

    But both of your examples are cleaner.

Leave a Reply