rubyguides

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.

See Also