Ruby: Diferença entre string e símbolo

Minha primeira dificuldade quando comecei a trabalhar com Ruby (on Rails) foi entender o que era aquele monte de : nos códigos, literalmente em todos os cantos. Mais difícil ainda foi entender as explicações nos primeiros resultados da busca "difference between string and symbols". Segui um bom tempo desenvolvendo código em Ruby sem ter entendido completamente a diferença entre essas duas classes, até que uma vez me deparei com um bug que foi causado por uma má decisão entre símbolo e string!

Ponto de partida

Vamos discutir ao longo do post os seguintes casos:

  1. Guardar dados
  2. Representar algum valor (do seu programa)

Com "guardar dados" sendo o caso de uso de strings, como por exemplo, registrar o nome de um livro.

titulo = 'O senhor dos anéis'

             

E o segundo caso: como assim representar um valor?

O uso dos símbolos são otimizados quando precisamos representar um valor que existe em nosso programa, como em uma associação do ActiveRecord:

has_one :book

ou em key de uma Hash (note também o uso da string para guardar um dado):

hash = { name: "Vinicius" }
hash[:name]
=> "Vinicius"

             

Certo, mas por que símbolos são otimizados pra esse papel de "Representar um valor"?

Dois motivos!

O primeiro é que só pode haver uma instância de um símbolo. O que isso quer dizer é que se eu mencionar o símbolo :name duas vezes no mesmo código, vai ser sempre o mesmo :name.

Por exemplo, se eu fizer:

a = :teste
b = a
c = :teste

Eu terei certeza que a, b e c se referem ao mesmo objeto. Além disso, a velocidade de comparação entre símbolos é extremamente rápida!

a == c # true
a === c # true
a.eql?(c) # true
a.equal?(c) # true

 
O que não acontece com strings, já que toda vez que você atribui o valor "teste" para novas variáveis, novas strings são criadas (ocupando memória extra do seu programa).

O segundo motivo é que símbolos são imutáveis. Não é possível alterar um símbolo como você faria com uma string: métodos como upcase! ou capitalize! não alteram o modificam. Benefício? Você tem a garantia que não terá uma parte do seu software modificando um símbolo por acidente (o que gera bugs difíceis de serem encontrados!).

Conclusão

Quando você precisa de todo o arsenal de métodos como o upcase, split, ou outros, e o principal motivo é o de guardar um dado, seja um nome, data, etc, utilize uma string!
Já quando o motivo é representar algo em seu código, como o nome de um método, ou o um valor dentro de uma Hash, utilize símbolos.

Ainda confuso? Não se preocupe! Use o método public_methods em uma versão pré-1.9 do Ruby e veja que até programadores experientes ainda se confundem as vezes em qual escolher. Como esse método retorna todos os métodos (públicos) de um objeto, você há de concordar que o correto seria public_methods retornar uma array de símbolos, certo? Pois é, e nessas versões, se você fizesse:

x = Object.new
puts x.public_methods

Você teria na verdade uma array de strings!

["inspect",
"pretty_print_cycle",
"pretty_print_inspect",
"clone",
...
]

  
Fonte: OLSEN, Russen. Eloquent Ruby

21