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:
:hello
NOTE: You'll see an example of a multi-word Symbol later.
When calling puts
on a Symbol:
puts :hello
It displays the following:
hello
But when calling p
on this Symbol:
p :hello
It 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 = :john
Also, 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 #=> :JOHN
In 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:Symbol
In 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 #=> 928360
In contrast, Symbols with the same content refer to the same object:
:john.object_id #=> 539688
:john.object_id #=> 539688
In 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:Symbol
A String can be centered, while a Symbol cannot.
'john'.center(6) #=> ' john '
:john.center(6) # NoMethodError: undefined method `center' for :john:Symbol
A String can be queried about its content, but a Symbol cannot.
'john'.include?('jo') #=> true
:john.include?(:jo) # NoMethodError: undefined method `center' for :john:Symbol
Strings 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.
yay! learnin' about Symbols :)
little chunks really help... thanks!