Nivel inicial6 min de lectura · Publicado el

Símbolos en Ruby: qué son y en qué se diferencian de String

Símbolos en Ruby: dos :ana apuntan a un único objeto, con el mismo object_id, y dos "ana" crean dos String distintos, con object_id 16 y 24. Un símbolo, un solo objetorubentejera.com :ana:ana16109836un objeto"ana""ana"1624dos objetos
Escribas :ana donde lo escribas, Ruby usa un solo objeto, congelado: el mismo object_id. Cada "ana" (sin frozen_string_literal) es un String nuevo, que se puede modificar: "ana" << " lópez".

Un símbolo en Ruby es un nombre que no cambia. Se escribe con dos puntos delante: :ana, :nombre, :activo. Durante toda la ejecución del programa, Ruby guarda un único objeto por cada nombre: escribas :ana donde lo escribas, es siempre el mismo.

Piensa en una etiqueta impresa y unas hojas de notas. La etiqueta «Facturas» es una sola y no la reescribes; las hojas (el texto) las puedes tachar, ampliar o sustituir. Los símbolos son las etiquetas; los String, las hojas.

Símbolo frente a String: la diferencia en código

Cuando escribes dos cadenas iguales, aunque tengan exactamente lo mismo, Ruby crea dos objetos. En cambio, si escribes dos símbolos iguales, Ruby no crea otro: reutiliza el que ya existía.

Lo comprobamos con object_id, el número que identifica a cada objeto dentro del programa:

cadena1 = "ana"
cadena2 = "ana"

simbolo1 = :ana
simbolo2 = :ana

puts cadena1.object_id   # 16
puts cadena2.object_id   # 24 (otro objeto)

puts simbolo1.object_id  # 16101644 (el número cambia según la versión)
puts simbolo2.object_id  # 16101644 (el mismo objeto)

Los números concretos cambian en cada ejecución y en cada versión de Ruby; lo que importa es si coinciden o no.

Igual contenido no es el mismo objeto

Ruby distingue dos preguntas:

  • ==: ¿tienen el mismo contenido?
  • equal?: ¿son el mismo objeto?
p cadena1 == cadena2          # true:  mismo texto
p cadena1.equal?(cadena2)     # false: dos objetos distintos
p simbolo1.equal?(simbolo2)   # true:  un único objeto

Para saber si dos cadenas son iguales, Ruby tiene que comparar su contenido. Con dos símbolos le basta con mirar si son el mismo objeto, porque nunca habrá dos símbolos distintos con el mismo nombre. Es como comprobar si dos llaves son iguales: con cadenas miras los dientes uno a uno; con símbolos, basta con ver si es la misma llave.

Un símbolo no se puede modificar

Un String se puede cambiar después de crearlo. Un símbolo, no: está siempre congelado (en Ruby, un objeto congelado o frozen es uno que no admite cambios).

nombre = "ana"
nombre << " lópez"
p nombre            # "ana lópez"

p :ana.frozen?      # true
:ana.upcase!        # NoMethodError: undefined method 'upcase!' for an instance of Symbol

Eso no significa que un símbolo no tenga métodos. Tiene los que no modifican nada, y devuelven un valor nuevo:

p :ana.length       # 3
p :ana.upcase       # :ANA (otro símbolo)
p :ana.to_s         # "ana" (de símbolo a String)
p "ana".to_sym      # :ana (de String a símbolo)
p :"con espacios"   # también se pueden crear con comillas
p %i[uno dos tres]  # [:uno, :dos, :tres]

Símbolo :ana

  • Un solo objeto por nombre
  • Siempre congelado
  • Para nombrar cosas: claves, opciones, estados

String "ana"

  • Puede haber muchos objetos con el mismo texto
  • Modificable, salvo que lo congeles
  • Para el texto con el que trabajas: nombres de usuario, mensajes, contenido

Símbolos como claves de un hash

El uso más habitual de los símbolos es como claves de un hash, la estructura de Ruby que guarda pares clave-valor (lo tienes explicado en Hashes en Ruby):

usuario = { nombre: "Ana", lenguaje: "Ruby" }

p usuario[:nombre]    # "Ana"
p usuario["nombre"]   # nil: la clave es un símbolo, no un String

La sintaxis nombre: "Ana" es la forma corta de :nombre => "Ana"; las dos crean una clave de tipo símbolo.

También puedes usar String como claves. Es seguro: cuando usas un String sin congelar como clave, Ruby guarda una copia congelada, así que cambiar la variable original no estropea el hash.

¿Cuándo se deben utilizar símbolos?

  1. Cuando no necesitas modificar el texto.
  2. Cuando el valor es un nombre de algo y no contenido: claves de hash, opciones de un método, estados como :activo o :pendiente.
  3. Cuando vas a comparar ese valor muchas veces.

Usa String cuando el texto viene de fuera (lo escribe el usuario, llega de un fichero o de una API) o cuando lo vas a transformar.

Símbolos para nombrar; String para el texto.

¿Ocupan memoria para siempre los símbolos?

Durante años se recomendaba no convertir en símbolos el texto que llega de fuera, porque los símbolos nunca se liberaban y la memoria podía crecer sin límite. Desde Ruby 2.2, la mayoría de los símbolos creados con to_sym o intern los puede liberar el recolector de basura (la parte de Ruby que libera la memoria que ya no se usa). Los que escribes directamente en el código, como :nombre, siguen viviendo mientras dure el programa.

Lo comprobamos creando mil símbolos y forzando una recolección:

GC.disable
antes = Symbol.all_symbols.size
1000.times { |i| "dinamico_#{i}".to_sym }
p Symbol.all_symbols.size - antes   # 1000

GC.enable
GC.start
p Symbol.all_symbols.size - antes   # 0: se han liberado

Aun así, crear símbolos a partir de lo que escribe un usuario rara vez tiene sentido: si es texto que viene de fuera, trátalo como String.

Cadenas congeladas: frozen_string_literal

Los símbolos son siempre inmutables; los String, no. Pero Ruby está cambiando cómo trata los String que escribes entre comillas en el código (los literales).

El comentario mágico

Si pones este comentario en la primera línea de un fichero, todos los literales de ese fichero quedan congelados:

# frozen_string_literal: true

s = "hola"
p s.frozen?                  # true
p "hola".equal?("hola")      # true: Ruby reutiliza el mismo objeto
s << " mundo"                # FrozenError: can't modify frozen String: "hola"

Con el comentario, los literales se parecen un poco más a los símbolos: el mismo texto es el mismo objeto y no se puede modificar. Hay dos excepciones útiles:

# frozen_string_literal: true

mutable = +"hola"            # + devuelve una copia modificable
mutable << " mundo"
p mutable                    # "hola mundo"

p "x#{1}".frozen?            # false: con interpolación no se congela

El comentario debe estar en la primera línea del fichero, o en la segunda si la primera es un shebang (#!/usr/bin/env ruby). Sobre la interpolación tienes más en Interpolación de cadenas en Ruby.

Sin el comentario: el aviso de Ruby 3.4 y 4.0

Desde Ruby 3.4, en los ficheros sin ese comentario, los literales se pueden seguir modificando, pero Ruby avisa de que dejará de permitirlo. El aviso solo aparece si activas las advertencias de obsolescencia con -W:deprecated:

ruby -W:deprecated literal.rb
literal.rb:3: warning: literal string will be frozen in the future (run with --debug-frozen-string-literal for more information)

En este estado, "hola".frozen? sigue devolviendo false. Con --enable=frozen-string-literal congelas todos los literales sin poner el comentario fichero a fichero, y con --disable=frozen-string-literal desactivas el aviso.

Lo mismo pasa con el texto que devuelve Symbol#to_s: en Ruby 4.0 avisa si lo modificas, y en las notas de la próxima versión (4.1, todavía en desarrollo) ya figura que devolverá un String congelado.

Resumen

Símbolo String
Se escribe :ana "ana"
Objetos con el mismo nombre Siempre uno Uno por literal (salvo con frozen_string_literal: true)
¿Se puede modificar? No, nunca Sí, salvo que esté congelado
Comparación ¿Es el mismo objeto? Compara el contenido
Uso típico Claves de hash, opciones, estados Texto de usuario, mensajes, ficheros
Convertir :ana.to_s "ana".to_sym

Fuentes

Comprobado el 7 de noviembre de 2026 en la documentación oficial de Ruby:

Todos los ejemplos se han ejecutado con Ruby 4.0.9 (imagen oficial ruby:latest de Docker) y las salidas de los comentarios son las reales.