What are Ruby Symbols?

by Charles Max Wood on July 27, 2009

A lot of new Ruby developers I’ve worked with have seen the symbol notation—starting with a :— and have been confused by what a symbol actually is. There is a lot of information out there that is confusing as well. Here’s a brief rundown of what symbols are and how they are used.

What a Symbol is and What it is not

When I first encountered symbols, someone told me that they were a simplified String without as much of the baggage. This isn’t the case. The Symbol class doesn’t inherit from the String class, nor does the String class inherit from the Symbol class.

According to The Ruby Way symbols actually have an immediate value, like Fixnum’s (numbers). Strings are like any other object. Each string is an instance of the String class. What this really means is that in the following example, the strings are stored in different locations in memory and the symbols refer to the same immediate value in memory.

Now that we’ve established that symbols aren’t strings, but a series of characters representing an immediate value, the real question is how to use them.

How We Use Symbols

There are a couple of common uses for symbols. They are by no means the only use for symbols and they aren’t uses exclusive to symbols.

As Keys for Hashes

A hash object is like an array except the index or key for each element in the hash is another object. For example, you can have a hash like this.

class Person
  attr_accessor :name
  def initialize(name)
    @name = name
  end
end
dave  = Person.new("Dave")
julie = Person.new("Julie")
joe = Person.new("Joe")
nikki = Person.new("Nikki")

girlfriends = { joe => nikki, dave => julie }

This example is a little contrived, but it shows that if you know about the object identified by the variable ‘joe’, you can find out that his girlfriend is ‘nikki.’

girlfriends[joe] #=> returns nikki

In most cases, having this kind of object as the key in a hash is overkill. If we know about joe, why not use a string or symbol as they key for the hash. After all, his name is “Joe.”

girlfriends = { :joe => nikki, :dave => julie }
girlfriends[:joe]

In this example, you still get the Person object back identifying Nikki, but in this case we don’t have the overhead of an object in the key or the hassle of making sure that this object representing Joe is an exact match to the one in the hash.

Symbols are also typically used in hashes identifying arguments passed to a method call. An excellent explanation of the best approach to this is in Chapter 2 of Ruby Best Practices.

Here’s a quick example from Ruby on Rails.

User.find(:all, :conditions => "active = 1", :include => [:posts, :friends])

ActiveRecord classes accept a hash as a parameter on the find method to identify what it is looking for in the query string. This also demonstrates the other two uses I’ll be covering in this post: as flags—representing certain behavior or state— and representing classes or methods.

As Flags—Representing Behavior or State

Have you ever managed a user system and used magic numbers to represent whether the user was pending, active, inactive, or deleted? 0, 1, 2, and 3 are simply not very descriptive. Wouldn’t it be nice to instead have the state represented by an object with an immediate value that was readable? Enter symbols!

user = User.new
user.status = :pending

This has two nice advantages. First, :pending means the same thing across your entire application as far as Ruby goes. So, there’s no going out of scope, etc. Second, you don’t have to answer the question “What’s the user’s status?” with “0, um, I mean ‘pending’.”

This covers state, but what about behavior? Let’s go back to our ActiveRecord example.

User.find(:all, :conditions => "active = 1", :include => [:posts, :friends])

The first argument :all, is not part of the hash. It represents the behavior—finding all users that match our conditions—that we want the find method to exhibit. Passing :first in there would produce a different result.

Representing a Class or Method

The best examples I can think of for classes come from Ruby on Rails and ActiveRecord. In an ActiveRecord object, you declare associations with other classes by representing the associated classes as symbols.

class User < ActiveRecord::Base
  has_and_belongs_to_many :roles
  belongs_to :group
  has_many :friends

:roles, :group, and :friends represent the Role, Group, and Friend classes.

Symbols represent methods in all kinds of places. This is usually done to call or find something out about the method. Here are some quick examples:

# Does the object have a method called jump?
object.respond_to?(:jump) #=> returns true if the method exists

# Call the private method initialize (this is usually only called by the "new" method)
# This is not a good idea on an object.
object.send(:initialize)

In Place of Strings

Despite the fact the symbols are not strings, in many cases—especially when parameters on a method— you can pass a string or a symbol. The two are basically interchangeable as long as you’re willing to call .to_sym on strings and .to_s on symbols.

You can’t always interchange strings and symbols, so be careful with this.

  • Allan

    “In this example, you still get the Person object back identifying Nikki, but in this case we don’t have the overhead of an object in the key”
    Isn’t (almost) everything in Ruby an Object? You’ll still have an object in the key (an instance of Symbol).
    I don’t understand the meaning of “immediate value”. Like primitives in Java?

    • http://charlesmaxwood.com Chuck

      The best way I can think of explaining immediate values is numerical values. For example 1 in Ruby is of class Fixnum. If you have a = 1 and b = a, then a and b both refer to the immediate value of 1. If you change it so that a = 2, b still equals 1 because it refers to the immediate value 1. If you have a and b both strings a = “person” and b = a. They both reference the same object. a.reverse! changes a’s stored value to “nosrep”. If you access b, it also contains “nosrep”. With objects of immediate value, you can only change the reference. You cannot change their value.

      With symbols, they are like Fixnum’s. You can refer to them in their immediate and immutable value, but you can’t change the Symbol. It’s like using 1. You can change your variables so they refer to 2, but then you’ve changed the reference, not the value itself. Incidentally, you can call to_i on a Symbol to convert it to it’s integer equivalent, but the integer doesn’t really actually tell you anything useful about the symbol.

  • Allan

    Thanks a lot!

Previous post:

Next post: