This content originally appeared on DEV Community and was authored by Hinokami Dev
Every time Ruby developers start talking about # frozen_string_literal: true, someone inevitably brings up symbols. It happens so often it’s almost a ritual. Many people assume symbols are just frozen strings wearing a different outfit; but that’s not actually true. Let’s unpack why this confusion exists and what really sets them apart.
What Symbols Really Are
A Symbol in Ruby is an immutable identifier.
It’s stored once in Ruby’s symbol table, and every reference to it points to the same object:
:status.object_id == :status.object_id # => true
Ruby uses symbols internally for method names, constants, and hash keys.
They’re meant to represent identity, not text.
If you think of them as labels "fast to compare, cheap to store" you’ll use them correctly.
What Frozen Strings Really Are
A frozen string is still a String object, just one you can’t modify:
s = "ready".freeze
s << "set" # => FrozenError: can't modify frozen String
Freezing avoids accidental mutation and reduces allocations.
But each literal remains a distinct string object:
"ready".object_id == "ready".object_id # => false
It’s immutable content, not a shared identifier.
Why They Get Confused
Both symbols and frozen strings are immutable and can improve performance by reducing new object allocations.
That’s where the similarity ends.
A symbol belongs to the Symbol class. Every unique symbol exists only once in memory. When you write :status five times, Ruby points to the same object each time.
A frozen string, on the other hand, is just a String object that can’t be changed. Each time you write "status", Ruby still creates a new string instance, even if it’s frozen. It’s immutable content, not a shared label.
Symbols created at runtime are garbage-collected since Ruby 2.2, but strings have always been managed by the garbage collector normally.
When handling data from users or external sources, always stick with strings. Turning unpredictable input into symbols can bloat memory, since every unique symbol name gets stored separately. Symbols are best reserved for fixed, internal identifiers; not user input.
Why It Matters
They may look the same, but they’re not interchangeable:
:status == "status" # => false
Freezing a string doesn’t make it a symbol, it just locks it.
If you need a key, event name, or label, use a symbol.
If you’re handling user input or text data, use a string.
Memory Notes
Older Ruby versions didn’t garbage-collect symbols, so creating dynamic ones could leak memory.
Ruby 2.2 and later fixed this. Runtime symbols are now GC-safe, but the “don’t dynamically symbol-ize user input” rule still holds for clarity.
The Takeaway
Symbols represent identity.
Frozen strings represent content.
Ruby’s frozen string literal magic comment was about making strings safer and faster, not about turning them into symbols.
So next time someone says “A frozen string is basically a symbol,” remember:
Not every frozen thing is a symbol.
This content originally appeared on DEV Community and was authored by Hinokami Dev
Hinokami Dev | Sciencx (2025-11-05T17:03:19+00:00) Why Ruby Devs Keep Mixing Up Symbols and Frozen Strings. Retrieved from https://www.scien.cx/2025/11/05/why-ruby-devs-keep-mixing-up-symbols-and-frozen-strings-2/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.