Strings & Symbols
Reviewed & published by Brayan K
Ruby is a dynamic, beginner-friendly programming language, and text is everywhere in real programs — Strings hold readable text while Symbols are fast, immutable labels.
Part of the free Ruby course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.
By the end of this lesson you'll build and transform strings, use interpolation, and understand exactly when to reach for a symbol instead.
What You'll Learn in This Lesson
- Building strings with interpolation and concatenation
- Essential String methods: upcase, strip, split, length
- Single vs double quotes and when each matters
- What a Symbol is and why it's lightweight
- How symbols differ from strings in memory
- Converting between strings and symbols
1️⃣ Working with Strings
Strings hold text. The friendliest way to build them is interpolation: inside double quotes, #{...} runs Ruby code and inserts the result. Because a String is an object, it comes with dozens of useful methods you call with a dot.
# Strings hold text. Use double quotes when you want interpolation.
greeting = "Hello"
name = "World"
# Interpolation: #{...} runs Ruby code and drops the result into the string.
puts "#{greeting}, #{name}!" # -> Hello, World!
# Concatenation joins strings with +
puts greeting + ", " + name + "!" # -> Hello, World!
# Handy String methods (everything is an object!):
puts "ruby".upcase # -> RUBY
puts "RUBY".downcase # -> ruby
puts " trim me ".strip # -> trim me
puts "hello".length # -> 5
puts "hello".reverse # -> olleh
puts "banana".include?("nan") # -> true
puts "a-b-c".split("-").inspect # -> ["a", "b", "c"]We used .inspect on the split result so you can see the array with its brackets and quotes clearly.
String Interpolation in Ruby: The Complete Rules
Interpolation is the piece beginners hit a wall on, almost always for the same reason: it only works inside double quotes. Single quotes are literal, so the placeholder prints as-is rather than being replaced. Every line below is real output from Ruby 3.3:
Three rules that save most of the remaining confusion:
- Any expression works, not just a variable. #{a + b}, a method call, even a whole block — Ruby evaluates it and drops in the result.
- to_s is called for you. An array becomes [1, 2, 3]; nil becomes an empty string, which is why a missing value silently vanishes instead of erroring. Use #{value.inspect} when you need to see the difference between nil and "".
- Case matters in the percent literals. %W interpolates, %w does not — the uppercase form behaves like double quotes.
Heredocs follow the same double/single-quote logic. A plain <<~EOS interpolates; quoting the terminator as <<~'EOS' turns the whole block literal — which is exactly what you want when writing out a template, a shell script or documentation that contains #{...} you do not want evaluated.
2️⃣ Symbols: Lightweight Labels
A Symbol is written with a leading colon, like :active. Think of it as a fixed name rather than editable text. Its superpower is identity: every mention of :active is the same object, so symbols are fast to compare and cheap to reuse. You'll see them constantly as hash keys and status flags.
# A Symbol looks like :name — a lightweight, immutable label.
# Symbols are often used as keys and identifiers, not as text to display.
status = :active
puts status # -> active
puts status.class # -> Symbol
# Why symbols? The SAME symbol is the SAME object every time,
# so they are fast and memory-friendly. Two strings are different objects;
# two identical symbols are literally the same object.
puts ("hi".object_id == "hi".object_id) # -> false (two String objects)
puts (:hi.object_id == :hi.object_id) # -> true (one Symbol object)
# Convert between them when needed:
puts "active".to_sym.inspect # -> :active
puts :active.to_s # -> activeYour turn. Build an ID badge that mixes strings and a symbol. Replace the three TODO values, then run it.
# 🎯 YOUR TURN — replace each ___, then run with: ruby badge.rb
first = ___ # 👉 replace ___ with "Grace"
last = ___ # 👉 replace ___ with "Hopper"
role = ___ # 👉 replace ___ with :admin
# 1) Build a full name with interpolation
full_name = "#{first} #{last}"
# 2) Print a badge line. Notice role is a symbol, so use to_s when displaying.
puts "Name: #{full_name}"
puts "Role: #{role.to_s.upcase}"
puts "Initials: #{first[0]}#{last[0]}" # [0] grabs the first character
# ✅ Expected output (for Grace Hopper, :admin):
# Name: Grace Hopper
# Role: ADMIN
# Initials: GHMini-Challenge: A Slug Maker
Turn a title into a URL-friendly slug using downcase and gsub. Run it with ruby slug.rb.
# 🎯 MINI-CHALLENGE: a slug maker.
# Save as slug.rb and run with: ruby slug.rb
#
# A "slug" is a URL-friendly version of a title.
# 1. Start with a title String, e.g. "Learn Ruby Fast"
# 2. Downcase it.
# 3. Replace spaces with dashes using .gsub(" ", "-")
# 4. Print the original and the slug.
#
# ✅ Example output:
# Title: Learn Ruby Fast
# Slug: learn-ruby-fast
# your code hereCommon Errors (and the fix)
- Interpolation in single quotes — 'Hi #{name}' prints literally. Use double quotes for #{...}.
- "no implicit conversion of Integer into String" — "Age: " + 30 fails. Use interpolation "Age: #{30}" or 30.to_s.
- Treating a symbol as text — :active.upcase works, but to display it call .to_s first when joining with strings.
- Comparing a symbol to a string — :a == "a" is false. Convert one side to match the other.
- Forgetting strings are zero-indexed — "hi"[0] is "h", not "i".
Pro Tips
- 💡 gsub for replacing: "a b c".gsub(" ", "_") swaps every match. Use sub for just the first.
- 💡 Multiline text: use a heredoc (<<~TEXT) for long, formatted strings.
- 💡 chomp the newline: when reading input with gets, call .chomp to strip the trailing newline.
📋 Quick Reference — Strings & Symbols
| Concept | Ruby Syntax |
|---|---|
| Interpolate | "Hi #{name}" |
| Concatenate | "a" + "b" |
| Upcase / strip | s.upcase / s.strip |
| Split / join | s.split(",") |
| Replace all | s.gsub("a","b") |
| String to symbol | "a".to_sym |
🎉 Lesson Complete!
- ✅ Strings hold editable text; build them with interpolation #{...}
- ✅ String methods like upcase, split, and gsub transform text
- ✅ Double quotes interpolate; single quotes are literal
- ✅ Symbols (:like_this) are fast, immutable labels
- ✅ Convert with to_sym and to_s
Practice quiz
How is a Symbol written in Ruby?
- With double quotes
- With single quotes
- With a leading colon, like :name
- With a trailing colon
Answer: With a leading colon, like :name. A Symbol is written with a leading colon, such as :name or :active.
Why are two identical symbols faster to compare than two identical strings?
- Every reference to the same symbol is the SAME object
- Symbols are sorted
- Symbols are shorter
- Strings are not comparable
Answer: Every reference to the same symbol is the SAME object. Each identical symbol is literally the same object in memory, so comparison and storage are cheap.
What does "hi".object_id == "hi".object_id evaluate to?
- true
- nil
- an error
- false
Answer: false. Two string literals are two separate objects, so their object_ids differ — the result is false.
What does :hi.object_id == :hi.object_id evaluate to?
- false
- true
- nil
- an error
Answer: true. :hi is always the same single object, so the object_ids match and the result is true.
Which kind of string literal performs interpolation with #{...}?
- Double quotes
- Single quotes
- Both
- Neither
Answer: Double quotes. Only double-quoted strings interpolate; single quotes treat #{...} as literal text.
How do you convert the string "active" into a symbol?
- "active".to_symbol
- Symbol("active")
- "active".to_sym
- :active.to_s
Answer: "active".to_sym. to_sym converts a string to a symbol; "active".to_sym gives :active.
What does :a == "a" return?
- true
- false
- nil
- an error
Answer: false. A symbol and a string are different types, so :a == "a" is false.
Are Symbols mutable or immutable?
- Mutable
- Mutable only in blocks
- Depends on the value
- Immutable (frozen)
Answer: Immutable (frozen). Symbols are immutable, lightweight labels — they are frozen and cannot be changed.
What is the recommended use for a Symbol versus a String?
- Symbols for editable display text
- Symbols for fixed identifiers like hash keys; Strings for arbitrary text
- Always use symbols
- Always use strings
Answer: Symbols for fixed identifiers like hash keys; Strings for arbitrary text. Use symbols for fixed, known identifiers (like :name); use strings for arbitrary or user-supplied text.
What does "hi"[0] return?
- "i"
- 0
- "h"
- "hi"
Answer: "h". Strings are zero-indexed, so [0] grabs the first character, "h".
Continue this course
- Previous: nil, true, false & Truthiness
- Next: Numbers & Math — Integers, floats, arithmetic, and handy numeric methods
- Quick reference: Ruby cheat sheet › Strings & Symbols
Frequently asked questions
What's the real difference between a String and a Symbol?
A String is mutable text meant to be read, changed, and displayed — "hello" can be upcased, reversed, or built from pieces. A Symbol like :hello is an immutable, lightweight label, typically used as an identifier such as a hash key or a status flag. Crucially, every reference to the same symbol points to the exact same object in memory, while two identical strings are two separate objects. That makes symbols faster to compare and lighter to store when you need a fixed name.
When should I use single quotes versus double quotes?
Use double quotes when you need interpolation (#{...}) or escape sequences like for a newline. Use single quotes for plain literal text where you want exactly what you typed — single quotes do not interpolate, so 'Hi #{name}' prints the #{name} literally. Many Ruby developers default to double quotes for consistency, but single quotes are slightly faster and clearer when no interpolation is needed.
How do I join strings together?
There are three common ways. Interpolation, "#{a} #{b}", is usually the cleanest and is preferred. Concatenation with + joins strings, as in a + b, but both sides must already be strings. The shovel operator << appends to an existing string in place. For combining many pieces — like a list — Array#join is ideal: ["a", "b", "c"].join("-") gives "a-b-c".
Are symbols garbage collected like strings?
Historically symbols were never garbage collected, which is why you should avoid creating symbols dynamically from untrusted input. In modern Ruby (2.2 and later) symbols created at runtime can be collected, so the old memory-leak worry is largely gone. Still, the guidance stands: use symbols for fixed, known identifiers (like :name or :active), and use strings for arbitrary or user-supplied text.