cult3

What are Symbols in Ruby?

Apr 22, 2015

Table of contents:

  1. What are Symbols?
  2. Working with Symbols
  3. Why would you use a Symbol instead of a String?
  4. When should you use Symbols?
  5. Conclusion

If you’ve been following along with the posts in this series you might have been wondering what a Symbol is.

You can recognise a Ruby Symbol because it will be a word that starts with a :.

So far we’ve seen them as the key in a Hash (Working with Hashes in Ruby):

person = { name: 'Philip' }

And as the arguments to a Struct (Working with Structs in Ruby):

Location = Struct.new(:longitude, :latitude)

When I first started learning Ruby, it wasn’t clear what Symbols represented or why you would use them.

Symbols in Ruby actually have a very explicit meaning and we use them for an important reason.

In today’s tutorial we will be looking at Symbols and why they are important in the Ruby language.

What are Symbols?

If you are approaching Ruby from a programming language like PHP or Javascript, the purpose of Symbols can seem unclear.

The ambiguity of Symbols is made worse as they seem to often be used interchangeably with Strings.

As we saw in Ruby Strings, the String data type in Ruby is special because it is actually an object.

A Symbol is basically the same as a String, but with one important difference. A Symbol is immutable.

In programming, when something is immutable it means it can’t be changed. Once you create an immutable object, it will remain exactly the same until it is destroyed.

This is very important in programming because mutable objects can lead to bugs that are hard to diagnose.

So a Symbol is essentially the same as a String, but it is immutable.

A good way of thinking about Symbols is, a Symbol is identifying something of importance, whereas a String is usually used to hold a word or a piece of text we need to work with.

Working with Symbols

To better understand Symbols in Ruby, let’s get our hands dirty and start to play around in IRB.

So the first thing we can do is to inspect a Symbol to see what class it uses:

:hello.class
# => Symbol

'hello'.class
# => String

So we can see that Symbols and Strings are instances of two different objects.

You can call String-like methods such as upcase, downcase and capitalize on Symbols:

:hello.upcase
# => :HELLO

:HELLO.downcase
# => :hello

:hello.capitalize
# => :Hello

I’m not entirely sure why would want to call these methods on a Symbol to be honest. I guess the Symbol Class must have them for a reason, but I don’t know what that reason is. If you do, please leave a comment.

However, if you attempt to modify a Symbol you will get an error:

:hello << ' world'
# NoMethodError: undefined method '<<' for :hello:Symbol

Why would you use a Symbol instead of a String?

So if a Symbol is just an immutable String, why would you use it, and why is there a special distinction in Ruby?

Symbols are Immutable

Firstly, one of the big reasons is, as I mentioned above, Symbols are immutable. Unforeseen bugs can crop up in your application when a value can change. If you need to ensure that the value of an object should never change, it’s much safer to use an immutable object.

However, with that being said, it is possible to make a String immutable in Ruby by calling the freeze method:

name = 'Philip'
# => "Philip"

name.freeze
# => "Philip"

name << 'Jim'
# RuntimeError: can't modify frozen String

As you can see in the example above, once you call the freeze method on a String instance, you can no longer modify it.

So why else would you use Symbols instead of Strings?

Symbols are better for performance

A second reason why you would use a Symbol over a String in certain situations is because Symbols are much better for performance.

For example:

'philip'.object_id
# => 70288511587360
'philip'.object_id
# => 70288504327720

:philip.object_id
# => 539368
:philip.object_id
# => 539368

When you create two String objects with the same value, those two objects are treated as two different objects. When you create a Symbol, referencing the Symbol will always use the same object.

This is much better for performance because the same String object will be created and destroyed over and over again when in reality the same object can just be reused each time.

When should you use Symbols?

So hopefully now you understand the characteristics of a Symbol and how they differ from regular String objects.

But when should you use Symbols in your code?

Well Ruby Symbols have a number of characteristics that make them perfect for certain jobs in your day-to-day coding.

Identifying things

You should use Symbols when you want to identify something.

For example a good use case of Symbols is for the keys of a Hash:

person = { name: 'Philip' }

We are identifying the name key here and so we don’t need the overhead of a mutable String.

Alternatively we would do the following:

person = { 'name' => 'Philip' }

But if we had 100 users, then Ruby would need to make 100 instances of the name String, when in reality we only need one because it is identifying the key in the Hash.

Another important thing to note is the following syntax:

person = { name: 'Philip' }

The above syntax looks a lot like JSON, however the key name is actually a Symbol, despite the : not being at the start of the word. This can be a bit confusing if you don’t know that’s the case.

You will also often see Symbols being used as the name parameters of a method:

Person.age(dob: '1982')

Again this is serving basically the same purpose as you don’t need the overhead of a String to represent the dob.

Used in method calls

Another use case is when you pass a Symbol into a method call, for example:

people = %w[Marty Emmett Bif]
# => ["Marty", "Emmett", "Bif"]

people.send(:pop)
# => "Bif"

When you send the Symbol :pop into the method send, the pop method will be called on the object.

You might also see this same technique used like this:

people.respond_to?(:map)
# => true

In this example we’re sending the :map to the respond_to? method. This will check to see if the map method is available on this object.

Another common usage of Symbols is when defining the attr_accessor of a Class:

class Dog
  attr_accessor :name
end

dog1 = Dog.new
dog1.name = 'Einstein'

In this example we’re sending the :name to the attr_accessor method. This basically just automatically creates getters and setters on the class.

Don’t worry about this too much for now as we’ll be covering creating Classes in a future tutorial.

When you want to set the status of something

A final common thing you will see Symbols used for is for setting the status of things:

class Car
  state :parked
  state :driving
  state :reversing
end

In the above class we’re saying that the Car can be in one of three states.

If you are familiar with other languages this is basically the same as using Enums.

Conclusion

Symbols can be confusing for newbie Ruby programmers. You will see Symbols used a lot in Ruby, and so it’s important to really understand why they are used.

Just remember, a Symbol is basically just a string that can’t be changed. This is perfect for identifying things like key’s in a hash that don’t need the overhead of a String object.

Hopefully if you’ve been struggling with Symbols whilst learning Ruby you will have found this useful. For me, getting my head around when and where to use Symbols was a big step towards fully understanding the Ruby language.

Philip Brown

@philipbrown

© Yellow Flag Ltd 2024.