ZetCode

Ruby Symbols

last modified October 18, 2026

Symbols are lightweight, immutable names in Ruby. A symbol is written with a leading colon, such as :name, and every occurrence of the same symbol refers to one shared object. This guide explains what symbols are, how they differ from strings, and how they are used as hash keys, method names, and blocks via Symbol#to_proc.

Symbol Literals

A symbol literal starts with a colon. Symbols containing spaces or special characters use the quoted form :"...". The %i[] notation builds an array of symbols from words separated by whitespace.

basics.rb
# A symbol is an immutable, interned name
status = :active
puts status              # prints: active
puts status.inspect      # prints: :active
puts status.class        # prints: Symbol

# Literal syntax: :name, :"with spaces", and %i[] lists
puts :"hello world".inspect
list = %i[red green blue]
p list
p list.map(&:class).uniq

The example creates a symbol with :active and shows that puts and inspect display it differently. The class method reports Symbol. It also prints a quoted symbol and builds a list with %i[].

$ ./basics.rb
active
:active
Symbol
:"hello world"
[:red, :green, :blue]
[Symbol]

Symbol vs String Identity

Two identical symbols are the same object, so equal? returns true. Two identical string literals are separate objects. Symbols are interned: Ruby stores one copy of each name and reuses it.

identity.rb
# Symbols with the same name are the same object
puts :ruby.equal?(:ruby)            # true
puts :ruby.object_id == :ruby.object_id  # true

# Strings with the same content are distinct objects
puts "ruby".equal?("ruby")          # false
puts "ruby".object_id == "ruby".object_id  # false

# Frozen string literals still create separate objects
a = "ruby"
b = "ruby"
puts a == b                         # true
puts a.equal?(b)                    # false
puts a.frozen?, :ruby.frozen?       # false, true

The example compares symbols and strings. Equal symbols are the same object, so equal? returns true. Equal string literals are separate objects, so equal? returns false.

$ ./identity.rb
true
true
false
false
true
false
false
true

Memory and Performance

Because each symbol name exists only once, using symbols for repeated identifiers such as hash keys avoids allocating many equal strings. Symbols are also frozen, so they cannot be modified in place. Comparing two symbols checks object identity, which is faster than comparing string contents. Choose strings when you need mutable text and symbols when you need stable names.

Converting with to_sym and to_s

to_sym converts a string to a symbol, and to_s converts a symbol back to a string. Converting a string to a symbol is useful for turning user input or configuration names into identifiers.

conversion.rb
# String to symbol with to_sym (or intern)
name = "username"
sym = name.to_sym
p sym                       # :username
puts sym.class              # Symbol

# Symbol to string with to_s (or id2name)
back = sym.to_s
p back                      # "username"
puts back.class             # String

# Round trip keeps the same value
puts "data".to_sym.to_s == "data"   # true
puts :data.to_s.to_sym.equal?(:data) # true

# Symbols with spaces need quoting
p "first name".to_sym       # :"first name"

The example converts between strings and symbols with to_sym and to_s. A round trip returns the original value, and a name with spaces needs quoting in symbol form.

$ ./conversion.rb
:username
Symbol
"username"
String
true
true
:"first name"

Symbols as Hash Keys and Method Names

Symbol keys (host:) are the idiomatic choice for hashes. A symbol key and a string key with the same text are different keys. The send and respond_to? methods accept method names as symbols, and define_method creates methods from a symbol name.

hash_methods.rb
# Symbols are the usual choice for hash keys
config = { host: "localhost", port: 8080 }
puts config[:host]          # localhost
puts config.key?(:port)     # true
p config.keys               # [:host, :port]

# Symbol keys vs string keys are different keys
mixed = { "host" => "a", host: "b" }
p mixed["host"]             # a
p mixed[:host]              # b
puts mixed.size             # 2

puts "hello".send(:upcase)            # HELLO
puts "hello".respond_to?(:upcase)     # true
puts "hello".respond_to?(:fly)        # false

class Robot
  %i[walk talk].each { |a| define_method(a) { "#{a}ing" } }
end
r = Robot.new
puts r.walk                 # walking
puts r.respond_to?(:talk)   # true

The example uses symbols as hash keys and as method names. send calls a method by name, respond_to? checks it, and define_method creates methods from symbols.

$ ./hash_methods.rb
localhost
true
[:host, :port]
"a"
"b"
2
HELLO
true
false
walking
true

Symbol#to_proc

The &:name shorthand passes a symbol as a block. Ruby calls the named method on each element. This is equivalent to writing an explicit block, and it works with any method that takes no arguments.

to_proc.rb
# Symbol#to_proc turns a symbol into a block
words = %w[ruby symbols are fast]
p words.map(&:upcase)       # ["RUBY", "SYMBOLS", "ARE", "FAST"]
p words.map(&:length)       # [4, 7, 3, 4]

# Equivalent explicit block
p words.map { |w| w.upcase }.equal?(words.map(&:upcase))  # false
p words.map { |w| w.upcase } == words.map(&:upcase)       # true

# Works with any method that takes no arguments
p [3, 1, 2].sort.map(&:to_s)  # ["1", "2", "3"]
p [[1, 2], [3]].map(&:size)   # [2, 1]

The example uses &:upcase, which passes the symbol as a block. It produces the same result as an explicit block, and it works with any method that takes no arguments.

$ ./to_proc.rb
["RUBY", "SYMBOLS", "ARE", "FAST"]
[4, 7, 3, 4]
false
true
["1", "2", "3"]
[2, 1]

Symbol Methods

Symbols support many of the same read-only methods as strings, including length and upcase, which return a new value. The <=> operator compares symbols alphabetically, and inspect shows the symbol in its literal form with the leading colon.

methods.rb
# Common Symbol methods
sym = :banana
puts sym.length              # 6
puts sym.upcase.inspect      # :BANANA
puts sym.to_s.capitalize     # Banana

# Comparison with <=> (symbols sort by their names)
p :apple <=> :banana         # -1
p :pear <=> :apple           # 1
p :kiwi <=> :kiwi            # 0
p %i[pear apple kiwi].sort   # [:apple, :kiwi, :pear]

# inspect shows the leading colon form
p :status                    # :status
puts :status.inspect         # :status
puts :status.to_s            # status

The example uses comparison and inspection methods on symbols. <=> orders symbols alphabetically, sort works on arrays of symbols, and inspect shows the leading colon.

$ ./methods.rb
6
:BANANA
Banana
-1
1
0
[:apple, :kiwi, :pear]
:status
:status
status

Source References

Learn more from these resources: Ruby Symbol Documentation, String#to_sym, and Proc Documentation.

List all Ruby tutorials.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.