Symbols in Ruby are used everywhere and for good reasons. There are two main reasons of using them instead of strings (however they are absolutely not a replacement to strings, more on that later).
1. Semantics
When you write :dog, it is clear that you are refering to something. On the opposite, when you write “dog”, it only looks like the letters d, o and g. If you remove the double quotes, dog magically gets a meaning.
2. Efficiency
When ruby sees a symbol for the first time, it creates a space in memory for it. Every other use of the same symbol will refer to that space instead of being allocated a new one. Same thing, same place.
This is all fine and dandy, but what are symbols exactly?
A symbol is an instance of class Symbol
In this regard it isn’t different than any other ruby object. The way it differs is in how you use the object. A Symbol instance is used differently than an instance of an other class. First, you cannot create a Symbol instance that way :
my_symbol = Symbol.new() # <== won't work!
To create a new Symbol instance, you simply have to use it like if it already existed. When ruby parses your code and finds a symbol for the first time, it creates an instance for it. The next time it will find the same object name (the name of the symbol) in your code, it will use the same space in memory (like any other object!).
So, to summarize : A symbol is simply an object with a special naming convention. The convention is that you must precede your symbol name with a colon ":" so ruby can understand that you want an instance of the Symbol class. Since Symbol.new() doesn't work, there had to be some way to create an instance of this class... that's exactly the purpose of the ":" character.
We don't care about what's contained inside a Symbol instance, we just care about it's name
While there are several instance methods you can call in a Symbol object, most of the time you are only interested by it's name. The purpose of a symbol is to centralize a meaning. You want to create names that will have a meaning for you and other programmers who may have to deal with your code later. You can think of symbols as an application wide registry table with 2 columns. The first column being the symbol object id (ex : 243938) and the second column being an associated name (ex : dog). Since we are humans and words have more meaning than numbers, we use the 2nd column to refer to a symbol.
Assignments
If you access a symbol directly, that is with the :symbol_name notation,you can't assign to it. It wouldn't make a lot of sense anyway. It's like if you were saying that the left object was a symbol and, at the same time, that it was a String. However, if you assign a symbol to an object, future assignments to this object are allowed.
:my_symbol = "blabla" # <== won't work!
container = :my_symbol # <== that's ok
container = :my_other_symbol # <== that's ok
container = "not a symbol" # <== that's ok
Comparing symbols to strings
If you write :dog == "dog", ruby returns false. It is because you are comparing a string with a Symbol instance. It just cannot be the same. If for some reason you would like to check if some string is equal to the name of a symbol, you would have to write :dog.to_s == "dog". Most of the time however, you will compare a Symbol instance with another Symbol instance.
my_var = :my_new_symbol #symbol created and assigned to my_var
my_other_var = :my_new_symbol #SAME symbol assigned to my_other_var
puts my_var==my_other_var #it's true!
puts my_var==:my_new_symbol #it's true!
Symbols are not better strings
Strings are all about content, symbols are all about meaning. When you write "dog", the three letters are the only things that matter. If you decide to use the String form of a "dog", it should be because you want to display the 3 letters on the screen or manipulate them in some way (capitalize, chomp!, reverse etc). When you write :dog, it is what a dog represents to your application that matters, not the individual letters. We only use words to define a symbol because we cannot use mental images (ruby is great, but that would be asking a bit too much...).
Symbols in hashes
You are probably comfortable using symbols with hashes already. animals[:dog] = "charlie" is something you see really often in ruby programs. As you might know, what the statement animals[:dog] = "charlie" really means to ruby is animals.[]=(:dog,"charlie"). The first parameter is a Symbol instance and the second parameter is the new value you want to store in the hash at the position associated with :dog. Every items in a hash are represented by a key and a value. In this specific case, the key is a Symbol instance. The []= method checks if there exists an item in the collection with the key part equals to the Symbol instance :dog. If it is the case, the 2nd parameter "charlie" is assigned to the value part of that same item.