Syntax and Variables

Ruby variables, types, symbols vs strings, string interpolation, arrays, hashes and control flow.

Variables

Ruby is dynamically typed — you never declare a variable's type, and a variable can be reassigned to a value of a completely different type at any time:

Ruby
name = "Ali"     # a String
name = 42        # now an Integer — perfectly legal, no error

Variable naming conventions in Ruby are meaningful, not just stylistic:

Prefix Meaning
name local variable
@name instance variable (belongs to an object)
@@name class variable (shared across all instances of a class)
$name global variable (accessible anywhere — rarely a good idea)
NAME constant (by convention; Ruby warns but doesn't prevent reassignment)

Basic types

Ruby
42.class          # Integer
3.14.class        # Float
"hello".class     # String
:hello.class      # Symbol
true.class        # TrueClass
nil.class         # NilClass
[1, 2, 3].class   # Array
{a: 1}.class      # Hash

Because everything in Ruby is an object, you can call methods directly on literals: 5.times { ... }, "hi".upcase, nil.to_s.

Symbols vs. strings

A symbol (:name) is an immutable, reusable identifier — internally, Ruby stores only one copy of a given symbol no matter how many times it appears, making symbols cheaper than strings for things like hash keys and method names:

Ruby
:status == :status          # true — same object, same identity
"status" == "status"        # true (same content) — but two separate String objects
"status".object_id == "status".object_id  # false — different objects each time

user = { name: "Ali", status: :active }   # symbols are the idiomatic choice for hash keys

Use a String when you need to manipulate text (concatenate, search, mutate). Use a Symbol when you need a lightweight, comparable label — a hash key, a status value, a method name reference.

String interpolation

Ruby
name = "Ali"
age = 22
puts "#{name} is #{age} years old"   # Ali is 22 years old
puts "Next year: #{age + 1}"          # any expression works inside #{}

Single-quoted strings do not interpolate — '#{name}' prints the literal text #{name}. Use double quotes whenever you need interpolation or escape sequences like \n.

Arrays

Ruby
fruits = ["apple", "banana", "cherry"]
fruits << "date"              # append — shovel operator
fruits.push("fig")            # equivalent to <<

fruits.first          # "apple"
fruits.last           # "fig"
fruits[1]             # "banana"
fruits[-1]            # "fig" — negative indices count from the end
fruits.length          # 5

fruits.each { |fruit| puts fruit }         # iterate with a block
doubled = fruits.map { |f| f.upcase }      # transform into a new array
short = fruits.select { |f| f.length < 5 } # filter

Hashes

Ruby
user = { name: "Ali", age: 22, active: true }

user[:name]               # "Ali"
user[:email] = "ali@example.com"  # add a new key
user.key?(:age)             # true
user.each { |key, value| puts "#{key}: #{value}" }

Control flow

Ruby
age = 20

if age >= 18
  puts "adult"
elsif age >= 13
  puts "teen"
else
  puts "child"
end

# unless — reads naturally as "if not"
unless age >= 18
  puts "not an adult"
end

# statement modifiers — very idiomatic Ruby for short conditionals
puts "adult" if age >= 18
puts "minor" unless age >= 18

# case/when — Ruby's pattern-matching switch
category = case age
           when 0..12  then "child"
           when 13..17 then "teen"
           else "adult"
           end
puts category

Loops and iteration

Idiomatic Ruby leans heavily on iterator methods over manual index-based loops:

Ruby
3.times { |i| puts i }             # 0, 1, 2

(1..5).each { |n| puts n }         # a Range, iterated

i = 0
while i < 3
  puts i
  i += 1
end

[1, 2, 3].each do |n|
  puts n * 2
end

The do...end and { } block forms above are interchangeable — the community convention is { } for short, single-line blocks and do...end for multi-line ones (covered in depth on the blocks page later in this track).

Common mistakes

  • Using a String as a hash key out of habit ("name" => "Ali") where a Symbol (name: "Ali") is idiomatic, faster to compare, and what virtually all Ruby/Rails code expects.
  • Forgetting single-quoted strings don't interpolate — 'Hello #{name}' prints the literal #{name}, not the variable's value.
  • Confusing nil with false in a condition — both are "falsy" in Ruby (everything else, including 0 and "", is truthy), which surprises people coming from languages where 0 is falsy.

Interview questions

Q: What's the difference between a Symbol and a String in Ruby? A Symbol (:name) is immutable and Ruby reuses the same object for every occurrence of that symbol, making comparisons cheap (identity comparison) and memory usage lower. A String ("name") is mutable, and each literal creates a distinct object even if the content is identical. Symbols are the idiomatic choice for hash keys and fixed identifiers; Strings are for text you'll manipulate.

Q: What values are "falsy" in Ruby? Only nil and false are falsy — everything else, including 0, "" (empty string), and [] (empty array), is truthy. This differs from languages like JavaScript or Python, where 0 and empty collections are also falsy.