Symbols
Symbols are immutable, interned identifiers — :status, :user_id. Cheaper than strings for hash keys / method names; the canonical “name of something.”
Symbols, comparison, conversions, hash keys
EXAMPLE
# 1) Literal
:status
:user_id
:'my-key with spaces'
# 2) Why symbols
# - Same content = same object (interned). Memory savings, fast comparison.
:foo.object_id == :foo.object_id # true (always)
'foo'.object_id == 'foo'.object_id # false (new string each time, in some modes)
# 3) Hash keys — use symbols for known fields
user = { id: 1, name: 'Ada', email: 'a@x.com' }
user[:name] # 'Ada'
# Old hash syntax (still valid)
user = { :id => 1, :name => 'Ada' }
# 4) Conversions
:hello.to_s # 'hello'
'hello'.to_sym # :hello
:hello.upcase # :HELLO
:hello.length # 5
:hello == :hello # true
:hello.equal? :hello # true (same object)
# 5) Symbols as method names
class User
attr_accessor :name, :email
end
User.instance_method(:name)
u = User.new
u.send(:name=, 'Ada')
u.send(:name) # 'Ada'
u.respond_to?(:name) # true
# 6) &:method shorthand — block from a symbol
names = ['ada', 'bo', 'cy']
names.map(&:upcase) # ['ADA', 'BO', 'CY']
names.map(&:length) # [3, 2, 2]
users.map(&:name) # like users.map { |u| u.name }
# 7) Compare strings vs symbols
'name' == :name # false — different types
:name.to_s == 'name' # true
# Use HashWithIndifferentAccess (Rails) when input may be either:
require 'active_support/core_ext/hash/indifferent_access'
h = { 'name' => 'Ada' }.with_indifferent_access
h[:name] # 'Ada' (works for both keys)
# 8) Common gotcha — symbols never get garbage collected before Ruby 2.2
# (Now: only frozen string literals and symbol-table entries from code, GC handles input symbols.)
# Still — don't generate symbols from untrusted input (DoS via memory growth).
user_input.to_sym # AVOID — attacker can fill symbol table
# 9) Keyword arguments use symbols
def create_user(name:, email:, role: :user)
# ...
end
create_user(name: 'Ada', email: 'a@x.com')
create_user(name: 'Cy', role: :admin)
# 10) Pattern matching with symbols (Ruby 3+)
case msg
in { type: :login, user: }
handle_login(user)
in { type: :logout }
handle_logout
in { type: :error, reason: String => r }
log_error(r)
end
# 11) Enumerable methods that take a symbol
numbers.reduce(:+) # sum
numbers.inject(:*) # product
strings.sort_by(&:length) # by length
rows.group_by(&:status) # group by status method
# 12) Hash with symbol keys — rich API
h = { name: 'Ada', age: 32, role: :admin }
h.each_pair { |k, v| puts "\#{k}=\#{v}" }
h.transform_keys(&:to_s) # string keys
h.slice(:name, :age) # subset
h.fetch(:role) { :user } # default block
h.dig(:profile, :city) # nested access
# 13) Use symbols for...
# - Hash keys when the set is known and finite (user[:name], user[:email])
# - Method names passed around (object.send(:method))
# - Status / enum values (:active, :paused, :banned)
# - Constants you'd write as :ROLE_ADMIN in C
# 14) Don't use symbols for...
# - User input that you'd parse to a symbol — DoS risk
# - Anything that needs mutation (symbols are immutable)
# - Display strings (they print weird with their colon)
# 15) Symbol procs in real code
users.select(&:active?).map(&:email)
emails = users.map(&:email).reject(&:nil?).uniq
# 16) Rails convention
# has_many :posts, dependent: :destroy
# validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP }
# render :index
# scope :recent, -> { order(created_at: :desc) }
# 17) Modern Ruby — symbol shorthand for hash literals (3.1+)
# Same name on both sides:
name = 'Ada'
email = 'a@x.com'
u = { name:, email: } # → { name: 'Ada', email: 'a@x.com' }
Why it matters
Symbols are Ruby’s “name of something.” Use for hash keys, method names, finite enums; avoid for user input (DoS risk) and display strings. &:method shorthand cleans up half your blocks.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Discussion
Loading…