Ruby: Lets talk Symbols
The other day, while I was reading an open source code, I kept seeing names with a colon in front of them. I just started with Ruby and I have been hooked. There is a lot i'm still not privy to semantically and syntactically. So, I tried guessing at first what those coloned-names represented. A special variable? Pointers? No, It can't be pointers. I doubt there is the concept of pointers in an interpreted language like Ruby. Scoped variable? maybe? or, maybe, global variable?
I got tired of guessing and consulted the omniscient google. Long story short, I learnt something new and I'm sharing it in this post.
What are symbols
The idea of symbols in ruby speaks to the whole concept of immutable strings. A symbol is a unique instance of the Symbol
class which is generally used for identifying a resource, that is, a method, a variable, a hashkey etc. It is unique, meaning, no matter where it appears in your program, a particular symbol have the same value.
You can think of Symbols
as, basically, strings that are immutable and, usually, represent something else.
Checkout the following code snippet. It is a usual representation of an associative array in php. right? So, in there, we have machine level strings recently_lost
, recently_found
as keys to represent associated values Recently lost
and Recently found
respectively. We use those strings as pseudo-keys to dynamically get their associated values.
Symbol is the dedicated structure built to encapsulate this idea in ruby.
$statuses = [
'recently_lost' => 'Recently lost',
'recently_found' => 'Recently found',
];
Ruby uses symbols, and maintains a Symbol Table to hold them. Symbols are names - names of instance variables, names of methods, names of classes. So if there is a method called control_movie, there is automatically a symbol :control_movie. Ruby's interpreted, so it keeps its Symbol Table handy at all times. You can find out what's on it at any given moment by calling
Symbol.all_symbols
. (Fabio Akita @ rubylearning.com)
Symbols
can be stored in variables just like strings can:
status = :lost
They can be converted to string and vice versa:
:lost.to_s
# 'lost'
'lost'.to_sym
# :lost
Symbols or Strings
Symbols are often compared to strings. The main difference between them relies on the fact that a new String
object is called for each string even if they are identical.
With strings, every time it appears in a program, a new object is created on its behalf. In the case of symbols, after creation, all other subsequent ones will reference the first one. This saves both time and memory. Let's investigate this claim:
"faruk".object_id
# 100
"faruk".object_id
# 200
Now do the same with symbols:
:name.object_id
# 71068
:name.object_id
# 71068
Attempting mutate a symbol will throw a NoMethodError
:
:lost.concat(' soul')
# Traceback (most recent call last):
# 4: from /Users/faruk/.rbenv/versions/2.7.2/bin/irb:23:in `<main>'
# 3: from /Users/faruk/.rbenv/versions/2.7.2/bin/irb:23:in `load'
# 2: from /Users/faruk/.rbenv/versions/2.7.2/lib/ruby/gems/2.7.0/gems/irb-1.2.6/exe/irb:11:in `<top (required)>'
# 1: from (irb):8
# NoMethodError (undefined method `concat' for :lost:Symbol)
If the contents of the object are of more importance, use string
. If the identity of the object is more important, use a symbol.
Using Symbols to create hashes
Hashes (sometimes known as associative arrays, maps, or dictionaries) are similar to arrays in that they are indexed collection of object references. However, while you index arrays with integers, you can index a hash with objects of any types: strings, regular expressions, and so on. When you store a value in a hash, you actually supply two objects - the index (normally called the key) and the value. You can subsequently retrieve the value by indexing the hash with the same key. The values in a hash can be objects of any type. (rubylearning.com)
In the event that you use a quoted string as a key of a hash value, it is advised that you use a symbol as in the following:
user = Hash.new
user[:name] = 'Faruk'
user[:gender] = 'Male'
user[:age] = 27
puts user[:name] # Faruk
This will greatly help with memory usage and performance. Especially in large applications dealing with huge amount of data.
And, that's all folks! Thank you for reading.
NB: If you want to share your thoughts with me concerning this post, please shoot an email to me: faruk at starfolksoftware dot com