July 2007 Archives

Ruby Structs for Immutable Types

Ruby's Struct class is a great way to define simple classes. For example, to define a simple Point class with getter and setter methods for x and y attributes, along with working ==, hash, and to_s methods, we can just write:

Point = Struct.new(:x,:y)

Struct was designed with mutable types in mind, but that doesn't mean that we are restricted to mutable types. Here's how we change the just-defined Point class to be immutable:

Point = Struct.new(:x, :y)  # Define mutable class
class Point                 # Open the class
  undef x=,y=,[]=           # Undefine mutator methods
end

Why do this for all of your Struct-based classes, however? Let's add a new factory method to Struct itself. If you want an immutable structure, use Struct.immutable instead of Struct.new:

def Struct.immutable(*args)  # Factory method for immutable classes
  Struct.new(*args).class_eval do # Define struct, then modify it
    undef []=                     # Undefine general mutator
    args.each do |sym|            # For each field of the struct
      mutator = :"#{sym.to_s}="   # Symbol for setter method
      remove_method mutator       # Undefine that method
    end
    self                          # Return the class
  end
end

Warning: I haven't actually tested this carefully, but it seems to work. Suggestions, corrections, etc., are welcome in the comments.

Books

Comprehensive coverage of Ruby 1.8 and 1.9

"The New Most Important Ruby Book"
Peter Cooper,
rubyinside.com

Completely updated for Ajax and Web 2.0

"A must-have reference"
Brendan Eich,
creator of JavaScript

The classic Java quick-reference