String#to_sym
str.to_sym -> symbol The to_sym method converts a string to its corresponding symbol. Symbols are immutable, interned strings that are efficient for use as hash keys, identifiers, and method names. Each unique string maps to a single symbol object, making to_sym useful for de‑duplication and fast comparison.
This is one of those conversions that is useful in the right place and risky in the wrong one. It shines when you are normalizing a small, known set of names such as configuration keys or method-like options. It can become a problem when you feed it untrusted or unbounded input, because the number of possible symbols can grow quickly.
Syntax
str.to_sym
The method takes no parameters. It returns a Symbol whose #to_s representation equals the original string. Since symbols are interned globally by the Ruby runtime, calling to_sym on identical strings always returns the exact same object, which makes symbol comparison an O(1) pointer check rather than a character-by-character scan. The examples below walk through basic conversion, interning behavior, edge cases, and practical patterns that show where to_sym fits in typical Ruby code.
Parameters
to_sym has no parameters.
Examples
Basic conversion
"hello".to_sym
# => :hello
"foo_bar".to_sym
# => :foo_bar
"123".to_sym
# => :"123" (symbol with numeric characters)
Numeric strings produce symbols that look like :"123" rather than :123, because the colon followed by digits is parsed as a keyword argument marker or a different syntax element. The quotes around the symbol name are required whenever the symbol would be an invalid literal. This is a minor presentation detail, but it is helpful to know when debugging code that converts user input containing numbers into symbol keys.
Symbols are interned
a = "hello".to_sym
b = "hello".to_sym
a.object_id == b.object_id
# => true (same symbol object)
c = "world".to_sym
d = "world".to_sym
c.object_id == d.object_id
# => true
The object_id comparison proves that to_sym returns the same object for identical strings. This interning behavior is the key reason symbols are efficient as hash keys and identifier tokens: comparing two symbols is an O(1) pointer comparison, not an O(n) string scan. The runtime does this automatically, so you get the performance benefit without any special handling in your code.
Edge cases: empty string and whitespace
"".to_sym
# => :"" (empty symbol)
" ".to_sym
# => :" " (symbol containing spaces)
While an empty string produces an empty symbol and whitespace produces a symbol containing spaces, both are unusual in everyday Ruby code. The real value of to_sym is that it works with any string content regardless of encoding or character set. Ruby’s symbol table stores entries as raw byte sequences, so accented characters, non-Latin scripts, and even emoji all become valid symbols without any special handling:
Non‑ASCII characters
"café".to_sym
# => :café
"🎉".to_sym
# => :🎉
Ruby handles Unicode characters in symbol names without any extra encoding steps — any valid UTF-8 string produces a valid symbol. This means you can use to_sym with international text, emoji, and special characters, though in practice most Ruby codebases stick to ASCII identifiers for readability. One of the most frequent uses of to_sym in real programs is converting string-keyed hashes into symbol-keyed hashes, which lets you use the familiar colon-style syntax for key access:
Using symbols as hash keys
hash = { "name" => "Alice", "age" => 30 }
hash.keys.map(&:to_sym)
# => [:name, :age]
Mapping hash keys from strings to symbols is a common step when the hash came from an external source like a parsed JSON file. The symbol keys let you use the shorter colon-style access syntax and make the hash consistent with Ruby’s internal conventions. Once you understand the basic conversion, the next set of examples show patterns that appear in configuration handling, deduplication, and lookup tables — all practical applications of to_sym in real Ruby programs:
Common Patterns
Converting user input to symbols for configuration
def parse_option(input)
case input.to_sym
when :enable
true
when :disable
false
else
raise "Unknown option: #{input}"
end
end
parse_option("enable") # => true
parse_option("disable") # => false
parse_option("invalid") # => raises "Unknown option: invalid"
The case statement above maps a small set of string values to boolean constants, turning the symbol into an internal dispatch token. For a bounded set of options like this, using symbols is appropriate because the number of possible inputs is known and limited. When you have a collection of strings and only care about distinct values, converting to symbols and calling uniq is a compact way to find the unique entries without writing a loop:
De‑duplicating strings in a collection
strings = ["foo", "bar", "foo", "baz", "bar"]
symbols = strings.map(&:to_sym).uniq
# => [:foo, :bar, :baz]
Calling uniq after mapping to symbols works because duplicate strings map to the same symbol object, so uniq sees them as equal. This is a lightweight way to normalize and deduplicate a list of identifiers without writing a custom comparison. When the data arrives as a hash with string keys — common when parsing JSON or reading configuration — converting those keys to symbols lets you use the familiar colon-style access syntax throughout the rest of the application:
Symbolizing hash keys (Rails‑style)
def symbolize_keys(hash)
hash.transform_keys(&:to_sym)
end
symbolize_keys({ "name" => "Alice", "age" => 30 })
# => {:name=>"Alice", :age=>30}
The transform_keys approach converts every key in the hash at once, which is convenient when the hash comes from an external source like a JSON parser or a YAML configuration file. When you only need to look up values by a symbol key, however, building the symbol up front and using a hash that already has symbol keys avoids the conversion entirely. The next example stores a small lookup table with symbol keys and converts the incoming string key only at the point of lookup:
Fast lookup with symbol keys
LOOKUP = { :en => "English", :fr => "French", :es => "Spanish" }
def translate(lang)
LOOKUP[lang.to_sym] || "Unknown"
end
translate("en") # => "English"
translate("fr") # => "French"
translate("de") # => "Unknown"
Errors
to_sym does not raise exceptions for any string input. However, note that symbols are not garbage‑collected in Ruby versions before 3.2, so converting uncontrolled user input to symbols can lead to a memory leak (symbol DoS). In Ruby 3.2 and later, dynamic symbols are garbage‑collected.
The rule of thumb is simple: use symbols for internal identifiers, but keep user-facing text as strings until you have a clear reason to convert it. That keeps the memory profile predictable and avoids turning casual input into a long-lived runtime object.
Potential memory concerns (pre‑3.2)
# In Ruby < 3.2, repeated conversion of arbitrary strings can cause memory growth
loop do
SecureRandom.hex(10).to_sym
end
The examples above are intentionally simple because the method itself is simple. The real decision is about trust and scale. If the input space is small and bounded, to_sym is a clean fit. If the input could be large or attacker-controlled, leave it as a string unless the conversion buys you something concrete.