Symbols vs Strings in Ruby

· 4 min read · Updated March 7, 2026 · intermediate
symbols strings ruby-basics intermediate

Ruby developers often encounter both symbols and strings in their daily coding. While they might look similar at first glance, understanding the difference between them is crucial for writing efficient and idiomatic Ruby code.

What Are Symbols?

A symbol in Ruby is a constant identifier that is immutable and unique. You create a symbol by prefixing a name with a colon:

:name
:this_is_a_symbol
:"symbol with spaces"

Symbols are commonly used as identifiers, hash keys, and method names. Unlike strings, symbols are cached in memory — every time you reference :name, you get the same object.

What Are Strings?

Strings are mutable sequences of characters. You create them with quotes:

"hello"
'also a string'
"string with #{interpolation}"

Each time you create a string, Ruby allocates new memory for it (unless you’re using string interning).

Key Differences

Immutability

Symbols are immutable — once created, their value cannot change:

symbol = :hello
symbol.upcase!  # NoMethodError: undefined method 'upcase!' for :hello:Symbol

Strings are mutable:

string = "hello"
string.upcase!  # => "HELLO"

Identity vs Value

Two strings with the same content are different objects:

"hello".object_id  # => 60
"hello".object_id  # => 80 (different object)

Two symbols with the same content are the same object:

:hello.object_id   # => 80
:hello.object_id   # => 80 (same object!)

This makes symbols ideal for hash keys.

Performance

Symbols are faster for equality checks because Ruby compares object IDs (O(1)), while strings must compare character by character (O(n)):

# Symbol comparison is just integer comparison
:status == :status  # Fast

# String comparison checks every character
"status" == "status"  # Slower for long strings

When to Use Symbols

Hash Keys

Symbols are the conventional choice for hash keys in Ruby:

user = {
  name: "Alice",
  email: "alice@example.com",
  role: :admin
}

user[:name]  # => "Alice"

This is faster and more idiomatic than using string keys like {"name" => "Alice"}.

Method Names and Identifiers

When referencing method names dynamically, use symbols:

.send(:upcase)
define_method(:greet) { puts "Hello!" }

Enum-like Values

Use symbols for fixed sets of values:

status = :pending

case status
when :pending then puts "Waiting..."
when :active then puts "Running!"
when :completed then puts "Done!"
end

When to Use Strings

Text Manipulation

When you need to modify the content, use strings:

message = "hello world"
message.capitalize!  # Modifies in place

User Input and Output

Strings are appropriate for data that comes from or goes to users:

puts "Enter your name:"
name = gets.chomp

Building Dynamic Content

When constructing messages or content dynamically:

greeting = "Hello, #{name}!"
html = "<p>Welcome, #{user.name}</p>"

Interpolation

Strings support interpolation while symbols do not:

type = "user"
:"#{type}_name"  # Works but unusual
"#{type}_name"   # Normal approach

Converting Between Symbols and Strings

You can convert between them:

:string.to_s   # => "string"
:string.to_sym # => :string

"string".to_sym  # => :string
:string.to_s    # => "string"

Be careful with user input — converting strings to symbols can expose you to symbol DoS attacks. Use to_sym sparingly with untrusted input.

Common Patterns

Symbol Hash Keys with Methods

When working with hashes that use symbol keys:

config = { theme: "dark", font_size: 14 }

config.each do |key, value|
  puts "#{key}: #{value}"
end

Dynamic Method Calls

Symbols are perfect for sending messages to objects:

class Calculator
  def add(a, b); a + b; end
  def subtract(a, b); a - b; end
end

calc = Calculator.new
operation = :add
calc.send(operation, 5, 3)  # => 8

Options Hashes

Methods often use symbol keys for options:

def configure(options = {})
  theme    = options[:theme]    || "light"
  font_size = options[:font_size] || 12
  # ...
end

configure(theme: "dark", font_size: 16)

Performance Implications

In Ruby 2.2+, symbols are no longer eternally garbage-collected (they were until Ruby 2.1). Now you can create symbols from user input more safely, but be mindful of memory:

# Each unique string becomes a symbol (consuming memory)
user_input = gets.chomp
user_input.to_sym  # Creates a new symbol each time

For untrusted input, use String#to_sym with caution. Consider alternatives like:

  • Using strings as hash keys
  • Using ActiveSupport::Symbol#to_param (if using Rails)
  • Implementing a symbol pool

Summary

AspectSymbolsStrings
MutabilityImmutableMutable
MemoryCached, sharedEach instance unique
ComparisonFast (by ID)Slower (by content)
Use caseIdentifiers, keysText, user data
Creation:name"name"

Use symbols for fixed identifiers, hash keys, and method names. Use strings when you need to manipulate text or work with user-generated content. This distinction will make your Ruby code more efficient and idiomatic.