1. Technology
You can opt-out at any time. Please refer to our privacy policy for contact information.

Symbols

By

Symbols

Symbols are used everywhere in Ruby code. They're most often used as hash keys, but in various other places as well. Most new Rubyists know they're something like strings, but how they differ exactly is a mystery. They have no real analog in most programming languages, they're one of the foreign concepts new Rubyists must learn before really understanding Ruby code.

What The Look Like

Symbols look like an identifier, much like a variable name of some kind, but with a colon character (:) at the beginning. For example, there can be a symbol called :name, or :valid, or they can start with capital letters like :Ruby or :Programmer. The thing to remember is that the always start with a colon, are most often lower case with snake_case (but can contain symbols, see below) and are one of the Ruby types that has its own literal syntax (like Strings, Regexp objects and numbers).

"The Thing Called"

When you see a symbol, you can read it as "the thing called." The symbol :name can be read as "the thing called 'name'." When used to index a hash, you can hash[:key] as "the thing called 'key' from the hash." Since methods in Ruby are not first class objects (you cannot assign them to variables or refer to them without Ruby internal methods), symbols are used to refer to a method when calling an internal method that can access the other methods. For example, in the send method the first parameter you pass is the name of the method as a symbol. The call some_obj.send(:juggle) can be read as "send the message called :juggle to the some_obj object."

This, more than else, reflects how symbols are used and their true purpose. Symbols are used to refer to things that are themselves not objects. Symbols are names for things. Yes, strings could be used, but there are some important reasons why strings are not used and symbols are used for this purpose.

What Symbols Really Are

On the surface, symbols and strings are very similar. But one difference you'll quickly discover is that symbols are immutable. Once you create a symbol, you cannot change it. You cannot append to it, capitalize it or change it in any way. You cannot change :level into :bevel or manipulate them in any way. Symbols are what they are. So, at first, you may think that symbols are a special type of string. That the symbol carries around the contents of the name used to refer to it, but this isn't the case, keep reading.

The next thing you'll notice is that only one of a symbol can ever exist. If in one part of your program you create the symbol object :ip_address and in a totally different part of the program, perhaps in a library that doesn't even know about your :ip_address symbol, you create another symbol from the same :ip_address literal, they'll both refer to the same object. What does it mean to be the "same?" Does the equality operator (==) return true? Yes, but they're even more equal. They're the same object with the same object ID. If this were a C struct, they'd have the same memory address. Only one of any Symbol object may ever exist.

"OK, that makes sense," you say. It's reasonable to assume that only one of a name can exist. There's no real need to keep more than one of such a thing around. But we're not done yet, it goes slightly deeper than that.

Behind the scenes when you create the :name symbol, the Ruby interpreter will search through a table of all the symbols that exist in the program. If the symbol was used before, it will retrieve an object ID from the table. If it doesn't find it, it will create a new object ID number and insert it into the table. The symbol is an object ID number and nothing more. At their heart, symbol objects are a large optimization of identifier strings. It turns a thing like :age into a simple number that takes very little memory and can be passed around very easily.

Creating Symbols

You can create Symbol objects with the symbol literal of course, but there are two (nearly identical) ways to create symbols. One is a modification of the literal syntax that allows you to use any character (including spaces) inside of the symbol string. The other is a way to convert any string into a symbol.

Most all symbol strings are lower case snake_case strings. There's nothing in them that would interfere with Ruby parsing your code. However, it's possible to make symbols with any identifying string, not just simple snake case words. To accommodate this, you can enclose your identifying string in quotes. You could say :ip_address or :"IP Address". Notice the double-quoted string, interpolation works here so you can say things like :"#{user.ip_address}". But there's a more clear way to say that (that also adds less line noise to your programs).

Any string can be converted into a symbol by calling the to_sym method. Instead of saying :ip_address you can say "ip_address".to_sym. This way, you can create symbols from user input, not just literals in your code. In the previous section, the example :"#{user.ip_address}" was given. Now that you know to_sym can be used, a more clear way to say the same thing would be user.ip_address.to_sym.

A Riddle

To get a list of all known symbols, call Symbol.all_symbols (the list will be rather large). How do you test for the presence of a particular symbol (for example, :hovercraft)?

"That's easy!" you say and fire up IRB. "I'll just use include? on the all_symbols array. Surely :hovercraft won't exist unless I create it, it's not a common word."


> Symbol.all_symbols.include?(:hovercraft)
 => true 

"Wait? It exists? I'm confused," you say. What happened there? When Ruby calls a method, it must first gather its parameters. One of the parameters you were passing was the symbol you were testing for. In testing for the presence of the symbol, you inadvertently created it. To test for a symbol without creating it, you could convert all the symbols to strings (using :some_symbol.to_s and test for the identifying strings. There is no real way to destroy symbols, so polluting the symbol space is possible. Luckily this isn't an issue in actual code, only in this riddle.


> Symbol.all_symbols.map(&:to_s).include?("hovercraft")
 => false 
> :hovercraft
 => :hovercraft 
> Symbol.all_symbols.map(&:to_s).include?("hovercraft")
 => true 

Notice the &:to_s trick? It's converting a symbol to a proc.

  1. About.com
  2. Technology
  3. Ruby
  4. Beginning Ruby
  5. Ruby's Basic Features
  6. Symbols

©2014 About.com. All rights reserved.