Symbols vs Strings in Ruby
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
| Aspect | Symbols | Strings |
|---|---|---|
| Mutability | Immutable | Mutable |
| Memory | Cached, shared | Each instance unique |
| Comparison | Fast (by ID) | Slower (by content) |
| Use case | Identifiers, keys | Text, 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.