The symbol in Ruby - Explanation
Tags: ruby, rails, programming, tutorial
Published: 2022-01-24 10:06:45

Everybody knows how to use it, everybody knows where, but why and what is its basis? What is a symbol in Ruby - I explain to myself and everyone who used it unknowingly so far. A few years ago, when I moved from .Net and C # to the Ruby world, I initially had trouble understanding what the symbols really were about. In one of the interviews, I got a question about the difference between a Symbol and a String and couldn't explain it. After total freaking out, I decided to explore the topic and now I am sharing with you the knowledge that I gained once and for all.

##What is a Symbol?

- an immutable object that is the name of another object
- unique for each name
- not associated with a specific instance of a given name while the program is running
- is a string that cannot be changed

##Symbol vs String

In Ruby, everything is an object, symbols and strings are objects, and instances of the Symbol class and the String class.

**The string is declared using double or single quotation marks.**

```ruby
"string" 'string'
```

**The symbol is defined by a colon before the literal.**

```ruby
:symbol
```

**Symbols can also contain quotation marks and apostrophes**

```ruby
:"symbol" :'symbol'
```

Remember that such an entry means Symbol, not String.

If we use the above notation, the symbol may contain spaces or other special characters. In traditional notation, _underscore_ is used as the separator.

**Symbols cannot be modified**

Strings can be modified, symbols are not. Symbols do not have _bang methods_ that change the object that calls them.

```ruby
"string".upcase #=> "STRING"

:symbol.upcase #=> :SYMBOL

"string".upcase! #=> "STRING"

:symbol.upcase!
NoMethodError: undefined method 'upcase!' for :symbol:Symbol

str = "string"
str = str << " sample" #=> "string sample"

sym = :symbol
sym = sym << " sample" #=> NoMethodError: unfined method '<<' for :symbol:Symbol
sym = sym << :sampel #=> NoMethodError: unfined method '<<' for :symbol:Symbol
```
##The role of symbols and strings

**The symbol** identifies something important, it is an object for unique identifiers.

**The string** is intended for storing words or pieces of text, it is a type dedicated for text data.

**Symbols and strings can be converted to each other**

```ruby
"string".to_sym #=> :string

:symbol.to_s #=> "string"
```
##Symbol and memory

![The_symbol_in_Ruby](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/115v4itbcgf7clkedrqx.png)

The same string has a different **object_id** every time, while the same symbol invoked three times returns the same value.

This is a key feature, plump meat, sweet flesh, a whole lot of symbols in Ruby.

Each string, although having the same value, is a separate instance, has its own **object_id** and its own memory location. **The symbol**, on the other hand, is unique, created only once while the program is running. Every time a symbol with the same value is called, one and the same instance is called.

This has an impact on code performance. Each such string takes additional memory space.

**Historical curiosity!** As of Ruby 2.2.0, Garbage Collector deals with symbols. Previously, the performance issue between String and Symbol was tricky. Namely, the symbols were stored in memory for the entire duration of the program, which in the case of dynamic symbol generation, e.g. from responses from external services in the application running without restart, for a long time led to a memory leak. As of Ruby 2.2.0 there is no need to worry about memory with numerous symbols. In the case of strings, the situation remains unchanged.

To understand how Garbage Collector deals with symbols, you need to understand the difference between explicitly declared symbols and dynamically created symbols.

##Explicitly Declared Symbols vs Dynamically Created Symbols

```ruby
:symbol #=> deklaracja
"symbol".to_sym #=> dynamiczna kreacja
```

Garbage Collector only cleaned up dynamically created symbols.

![The_symbol_in_Ruby](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/epzbybsvzem25u5o67l6.png)

Garbage Collector implementation in Ruby 2.2.0 and higher allows defending against the phenomenon called **DoS symbol - denial of service attack symbol.**

**A Dos symbol** is when the system dynamically creates a lot of symbols, eg by processing the Json response from an API. Memory is leaking.

Despite the improvement of the Garbage Collector mechanism, creating symbols from the received data will never be completely safe, e.g. when creating a method dynamically from parameters:

```ruby
define_method(params[:method].to_sym) do
end
```

This symbol was dynamically created to trigger the method. When it is further used, after being destroyed by the Garbage Collector, it will no longer be possible to call this method.

The same String, used several times, will be instantiated and destroyed each time, and **Symbol**, because it occupies one, permanent place in memory, will use the same instance for the entire duration of the program. It is similar to the case of a **symbol** created dynamically until it is marked for destruction by the Garbage Collector.

##The efficiency with equality operators

Memory issues become apparent when comparing Strings and Symbols. Since strings are variable and each string has an **object_id**, Ruby never really knows what the value of a particular String is, so it has to check character by character. When comparing two Strings, Ruby must check both sequences of characters to see if they have the same values and if they are arranged in the same order. **Comparing object identifiers will produce a skewed result because two identical strings with the same value have different object_id.**

Comparing **the Symbols** is completely different. The symbols do not change. **Two symbols with the same value have the same object_id.** Due to this immutability, Ruby is sure that **the Symbol has not changed** over the course of the program, so there is no need to compare character sequences. All you need is a quick comparison of object identifiers. The whole operation happens much faster than with strings.

Let the numbers speak for themselves…

```ruby
require 'benchmark'

str = Benchmark.measure do
        1_000_000.times do
          "string"
        end
      end.total #=> 0.07

sym = Benchmark.measure do
	      1_000_000.times do
          :symbol
	      end
      end.total #=> 0.39999999999999994

string_comparison = Benchmark.measure do
	                    1_000_000.times do
		                    "string" == "string"
	                    end
                    end.total #=> 0.13

symbol_comparison = Benchmark.measure do
	                    1_000_000.times do
		                    :symbol == :symbol
	                    end
                    end.total #=> 0.4999999999999999
```

##Storage of Symbols

There is one more performance aspect worth noting. All symbols are stored in memory in one collection.

We have access to it via:

```ruby
Symbol.all_symbols
```

How does this impact the performance? Every time the same String is called, it is instantiated and destroyed after use.

When calling symbols, it looks like Ruby checks the global collection first, and if it finds the symbol there, returns it immediately. If it does not find it, then it creates a new instance, also placed in the symbol collection, and returns it faster the next time, because it will be available there. **Symbols are efficient to compare, store, and use.**

##Symbol Simulation

Ruby gives you the ability to simulate the behaviour of a Symbol on a String by calling `the freeze` method. This makes string an invariant constant. However, this does not impact the memory. Two frozen strings are still two different instances. A frozen object, like a Symbol, cannot be modified. It is instantiated only once for each individual instance, then it is called for use.

**Curiosity!**

Symbol, fixnum, bignum, float are frozen by default. By using frozen strings as Hash keys, we can get better code performance than with symbols.

##Where to use the Symbols?

##There is no clear definition of when to use Strings and when to use Symbols.

![The_symbol_in_Ruby](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m9tjrx1aaan1vlenr4a8.gif)

The most common example of using symbols are Hash keys. 
The allowed notation is:

```ruby
old_hash = { :symbol_key => "some value" }
```

or

```ruby
cool_hash = { symbol_key: "some value" }
```

The second example shows the currently adopted _new_ notation.

**Why is it better to use symbols as keys?**

Because they present unique values that are not static, while strings are the default containers for data. Therefore, **Symbol fits better as an identifier**. As is the case with Hash keys. If we use a String as a key, Ruby will create a new instance each time it is invoked. In the case of symbols, one and the same object will be used.

If we have a hash declared with symbols as keys, we cannot access them with strings, the other way around is the same.

There is an object in Ruby on Rails that allows you to get to values using symbols and strings on a single Hash.

**HashWithIndifferentAccess**

The object is **HashWithIndifferentAccess**. Using symbols with this object is much faster than using a regular Hash.

```ruby
hash = {} #=> {}
hash[:symbol] = 1 #=> 1
hash[:symbol] #=> 1
hash['symbol'] #=> nil

hash = HashWithIndifferentAccess.new #=> {}
hash[:symbol] = 1 #=> 1
hash[:symbol] #=> 1
hash['symbol'] #=> 1
```

**Symbols are also keywords for method arguments**

```ruby
def method_name(name:, age:)
end

method(name: "John", age: 12)
```

**Symbols are used to define attributes in classes**

```ruby
attr_accessor :name, :age
```

**Symbols indicate an instance of classes or methods**

When declaring a class, method or variable, Ruby automatically creates its name in the form of a Symbol, hence the following entries are possible:

```ruby
my_array.send(:pop)
my_array.respond_to?(:map)
```

Interestingly, if we have a class called **Controller**, a method of **controller**, and a variable **controller**, and although in different contexts this name will refer to different values, this object will be called every time.

**Symbols in enum in Ruby on Rails**

There is an enum object in Ruby on Rails that uses symbols to define acceptable values for a particular field.

##What is worth remembering?

I have encountered a case of confusing symbols with variables, which is a shameful mistake. **The symbol is not a variable but a name** or otherwise **named object**.

It is important to remember that Symbol is a special class in Ruby that is used to define permanent, unique names.

Variables, on the other hand, are names that refer to objects. At the moment when the object ceases to exist - the variable also ceases to exist.

Different with the Symbol. It exists even if the object it is named does not exist.

**A symbol is not a variable - you cannot assign it a value.**

Another interesting thing is that although the Symbol is not a String, it has a string representation which is a constant. If we have a Symbol written with quotation marks, then its string representation will also have spaces.

```ruby
:"it is some symbol": "it is some symbol".to_s #=> "it is some symbol"
```

However, symbols with spaces are not commonly used and when there is no other possibility of writing, they are used as a last resort.

##A few simple rules

Finally, a few more simple rules when using Strings and Symbols, if that was still a problem. Therefore, in addition to the examples above, please note that:

- when we want to display something we should choose String
- if we want to process the name somehow, use modifying methods on it, we know that we use String
- when the name is meant to be a unique identifier, we use Symbol
- when the same name is to be used multiple times, we use Symbol

Symbols are very noble objects in the Ruby world. Their skilful use gives only advantages, from code readability to performance and memory savings.

For more details, please refer to [the Symbol class documentation](https://ruby-doc.org/core-2.7.1/Symbol.html)


![The_symbol_in_Ruby](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7kgmekoouc194amnepqy.gif)