In Ruby, a Symbol is the most efficient way, in terms of time and memory, to represent a set of characters.
Most commonly, a Symbol is a single word prefixed by a colon:
:helloNOTE: You'll see an example of a multi-word Symbol later.
When calling puts on a Symbol:
puts :helloIt displays the following:
hello
But when calling p on this Symbol:
p :helloIt displays the following:
:hello
A String can be assigned to a variable:
name = 'john'Similarly, a Symbol can be assigned to a variable too:
name = :johnAlso, a String can receive a variety of methods:
name = 'john'
name = name.upcase #=> 'JOHN'In this example, a copy of the 'john' String was created where each letter was capitalized to form 'JOHN'. Afterwards, the 'JOHN' String was reassigned to the name variable, replacing 'john'. In other words, two unique Strings were created.
Similarly, a Symbol can receive a variety of methods too:
name = :john
name = name.upcase #=> :JOHNIn this example, a copy of the :john Symbol was created where each letter was capitalized to form :JOHN. Afterwards, the :JOHN Symbol was reassigned to the name variable, replacing :john. In other words, two unique Symbols were created.
A String is mutable. That means its value can change while a program is running:
name = 'john'
name.upcase! #=> 'JOHN'In this example, the 'john' String itself was capitalized to 'JOHN'. No copies were made. And because only the value of the String has changed, the name variable doesn't need to be reassigned. In other words, one String was created and altered.
Unlike a String, a Symbol is immutable. That means its value remains constant during the entirety of the program:
name = :john
name.upcase! # NoMethodError: undefined method `upcase!' for :john:SymbolIn this example, there is no upcase! method for the :john Symbol. It's not possible to change the value of a Symbol like you can a String. In other words, once created, a Symbol cannot be altered.
Strings with the same content refer to a different object:
'john'.object_id #=> 927900
'john'.object_id #=> 928360In contrast, Symbols with the same content refer to the same object:
:john.object_id #=> 539688
:john.object_id #=> 539688In other words, two Strings with the exact same content are two different objects. However, two Symbols with the exact same content are only one object.
Every time a program creates an object, it takes time and memory. This means Symbols are more efficient than Strings.
For this reason, Symbols make great Hash keys:
person = { :name => 'john' }
person[:name] #=> 'john'In this example, the first time the Ruby interpreter sees :name, it creates a Symbol. The next time it encounters :name, it reuses the same Symbol saving precious time and memory.
In fact, this is so common, there's a shorthand way of writing the above example:
person = { name: 'john' }
person[:name] #=> 'john'In this example, the colon moved to the right of the word name and the hash rocket => disappeared.
TIP: This syntax for Symbols, where the colon is to the right of the word, is only available when defining a Hash using curly braces {}.
If Symbols are more efficient than Strings, why bother with Strings at all?
One reason is that Strings have a richer set of methods.
For example, two Strings can be added together, but two Symbols cannot.
'jo' + 'hn' #=> 'john'
:jo + :hn # NoMethodError: undefined method `+' for :jo:SymbolA String can be centered, while a Symbol cannot.
'john'.center(6) #=> ' john '
:john.center(6) # NoMethodError: undefined method `center' for :john:SymbolA String can be queried about its content, but a Symbol cannot.
'john'.include?('jo') #=> true
:john.include?(:jo) # NoMethodError: undefined method `center' for :john:SymbolStrings have many methods, like the ones above, that make working with textual content much easier.
Quite easily, actually:
'john'.to_sym #=> :john
:john.to_s #=> 'john'In both cases, a copy is made of the content and either a new Symbol or String is returned.
Less commonly, a Symbol is also multiple words, wrapped inside quotation marks and prefixed by a colon:
:'john lennon'When calling puts on a multi-word Symbol:
puts :'john lennon'It displays the following:
john lennon
But when calling p on this Symbol:
p :'john lennon'It displays the following:
:"john lennon"
NOTE: Ruby always uses double quotation marks when displaying multi-word Symbols.