Skip to content

Ruby Variables

Variables are fundamental concepts in programming used to store and manipulate data. Ruby provides multiple types of variables, each with specific scope and usage. This chapter will introduce in detail the various variable types in Ruby and how to use them.

📦 Variable Types Overview

Ruby has four main variable types:

  1. Local Variables - Used within methods or code blocks
  2. Instance Variables - Belong to object instances
  3. Class Variables - Belong to the class itself
  4. Global Variables - Accessible throughout the program

🏠 Local Variables

Basic Concepts

Local variables are only visible within the methods or code blocks where they are defined, starting with lowercase letters or underscores.

ruby
# Local variable definition
name = "Zhang San"
age = 25
_score = 95

# Using local variables in methods
def calculate_area(length, width)
  # length and width are parameter local variables
  area = length * width  # area is a local variable within the method
  area  # Return value
end

puts calculate_area(5, 3)  # 15

Scope

ruby
def method1
  local_var = "Local variable in method 1"
  puts local_var
end

def method2
  # puts local_var  # Error: Cannot access local variables from other methods
  local_var = "Local variable in method 2"
  puts local_var
end

method1  # Output: Local variable in method 1
method2  # Output: Local variable in method 2

Local Variables in Code Blocks

ruby
# Code blocks can access external local variables
outer_var = "External variable"

[1, 2, 3].each do |item|
  # item is a code block parameter
  puts "#{outer_var}: #{item}"
end

# Output:
# External variable: 1
# External variable: 2
# External variable: 3

# Use semicolons to separate code block local variables
[1, 2, 3].each do |item; block_local|
  block_local = item * 2
  puts "#{item} * 2 = #{block_local}"
end

Variable Assignment and Parallel Assignment

ruby
# Basic assignment
x = 10
y = 20

# Parallel assignment
a, b = 1, 2
puts "a = #{a}, b = #{b}"  # a = 1, b = 2

# Swap variable values
a, b = b, a
puts "a = #{a}, b = #{b}"  # a = 2, b = 1

# Array destructuring
first, *rest = [1, 2, 3, 4, 5]
puts "first = #{first}"    # first = 1
puts "rest = #{rest}"      # rest = [2, 3, 4, 5]

# Ignore certain values
_, second, _ = [1, 2, 3]
puts "second = #{second}"  # second = 2

🎯 Instance Variables

Basic Concepts

Instance variables belong to specific object instances, starting with @ symbol, and exist throughout the object's lifecycle.

ruby
class Person
  def initialize(name, age)
    @name = name    # Instance variable
    @age = age      # Instance variable
  end
  
  def introduce
    puts "I am #{@name}, I am #{@age} years old"
  end
  
  def have_birthday
    @age += 1       # Modify instance variable
    puts "Happy birthday! Now #{@age} years old"
  end
  
  def name
    @name           # Access instance variable
  end
end

person = Person.new("Zhang San", 25)
person.introduce        # I am Zhang San, I am 25 years old
person.have_birthday    # Happy birthday! Now 26 years old
puts person.name        # Zhang San

Instance Variable Scope

ruby
class Counter
  def initialize
    @count = 0        # Each instance has its own @count
  end
  
  def increment
    @count += 1
  end
  
  def count
    @count
  end
  
  def reset
    @count = 0
  end
end

counter1 = Counter.new
counter2 = Counter.new

counter1.increment
counter1.increment
puts counter1.count  # 2
puts counter2.count  # 0 (different instances)

counter2.increment
puts counter2.count  # 1
puts counter1.count  # 2 (not affected)

Instance Variable Default Values

ruby
class Example
  def initialize
    # Uninitialized instance variables default to nil
    puts @uninitialized.inspect  # nil
  end
  
  def set_value
    @value = "Set"
  end
  
  def get_value
    @value  # Returns nil if not set
  end
end

example = Example.new
puts example.get_value.inspect  # nil

example.set_value
puts example.get_value          # Set

🏢 Class Variables

Basic Concepts

Class variables belong to the class itself, shared by all instances of that class, starting with @@ symbol.

ruby
class Counter
  @@total_count = 0    # Class variable
  
  def initialize
    @instance_count = 0
    @@total_count += 1
  end
  
  def increment
    @instance_count += 1
  end
  
  def instance_count
    @instance_count
  end
  
  def self.total_count
    @@total_count      # Access class variable
  end
  
  def self.reset_total
    @@total_count = 0  # Modify class variable
  end
end

# Create instances
counter1 = Counter.new
counter2 = Counter.new
counter3 = Counter.new

puts Counter.total_count  # 3

counter1.increment
counter1.increment
puts counter1.instance_count  # 2
puts counter2.instance_count  # 0 (different instance variables)

# All instances share class variables
puts Counter.total_count  # 3

Class Variable Inheritance

ruby
class Parent
  @@class_var = "Parent variable"
  
  def self.get_class_var
    @@class_var
  end
  
  def self.set_class_var(value)
    @@class_var = value
  end
end

class Child < Parent
  # Subclass shares parent's class variable
end

puts Parent.get_class_var   # Parent variable
puts Child.get_class_var    # Parent variable

# Modifying class variable affects all classes
Child.set_class_var("Modified value")
puts Parent.get_class_var   # Modified value
puts Child.get_class_var    # Modified value

Class Variable Considerations

ruby
class A
  @@value = 1
  
  def self.value
    @@value
  end
end

class B < A
  @@value = 2  # Modifies parent's class variable
end

class C < A
  # Inherits the modified value
end

puts A.value  # 2 (modified by B)
puts B.value  # 2
puts C.value  # 2

# This behavior can lead to unexpected results

🌍 Global Variables

Basic Concepts

Global variables are accessible throughout the entire program, starting with $ symbol.

ruby
# Define global variable
$global_counter = 0

class GlobalCounter
  def increment
    $global_counter += 1
  end
  
  def self.global_count
    $global_counter
  end
end

# Access global variables anywhere
puts $global_counter  # 0

counter1 = GlobalCounter.new
counter2 = GlobalCounter.new

counter1.increment
counter2.increment
counter1.increment

puts GlobalCounter.global_count  # 3
puts $global_counter             # 3

Predefined Global Variables

ruby
# Ruby provides some predefined global variables
puts $0        # Current script name
puts $:        # Load path ($LOAD_PATH alias)
puts $"        # List of loaded files ($LOADED_FEATURES alias)
puts $$        # Current process ID
puts $?        # Last executed child process exit status
puts $!        # Last raised exception
puts $@        # Backtrace of last raised exception

# Input/output related
puts $_        # String last read by gets
puts $.        # Last read file line number
puts $;        # Default separator for split

Alternatives to Global Variables

ruby
# Avoid overuse of global variables; use module constants instead
module AppConfig
  DEFAULT_TIMEOUT = 30
  MAX_RETRIES = 3
  DEBUG_MODE = false
  
  def self.timeout
    @timeout ||= DEFAULT_TIMEOUT
  end
  
  def self.timeout=(value)
    @timeout = value
  end
end

puts AppConfig::DEFAULT_TIMEOUT  # 30
AppConfig.timeout = 60
puts AppConfig.timeout           # 60

🔤 Constants

Basic Concepts

Constants start with uppercase letters, typically all uppercase, used to store values that should not change.

ruby
# Define constants
PI = 3.14159
MAX_SIZE = 100
DEFAULT_NAME = "Anonymous User"

class MathConstants
  E = 2.71828
  GOLDEN_RATIO = 1.61803
  
  def self.calculate_circle_area(radius)
    PI * radius * radius
  end
end

puts PI                          # 3.14159
puts MathConstants::E            # 2.71828
puts MathConstants.calculate_circle_area(5)  # 78.53975

Constant Scope

ruby
OUTER_CONSTANT = "Outer Constant"

class MyClass
  INNER_CONSTANT = "Inner Constant"
  
  def self.show_constants
    puts OUTER_CONSTANT      # Can access outer constant
    puts INNER_CONSTANT      # Can access inner constant
  end
end

puts OUTER_CONSTANT          # Outer Constant
# puts INNER_CONSTANT        # Error: Cannot directly access class internal constant
puts MyClass::INNER_CONSTANT # Access via class name

MyClass.show_constants       # Display both constants

Reassigning Constants

ruby
# Ruby allows reassigning constants, but issues a warning
WARNING_LEVEL = 1
puts WARNING_LEVEL  # 1

WARNING_LEVEL = 2   # warning: already initialized constant WARNING_LEVEL
puts WARNING_LEVEL  # 2

# Freeze object to prevent modification
FROZEN_ARRAY = [1, 2, 3].freeze
# FROZEN_ARRAY << 4  # RuntimeError: can't modify frozen Array

🔄 Variable Scope Details

Nested Scope

ruby
def outer_method
  outer_var = "Outer method variable"
  
  def inner_method
    # Cannot access outer_var
    # puts outer_var  # Error
    inner_var = "Inner method variable"
  end
  
  inner_method
  # puts inner_var    # Error: Cannot access inner method's variables
end

# Code block scope
def block_scope_example
  outer_value = "Outer value"
  
  [1, 2, 3].each do |item|
    block_var = "Block variable"
    puts "#{outer_value} - #{item} - #{block_var}"
  end
  
  # puts block_var  # Error: Cannot access block variable
end

block_scope_example
# Output:
# Outer value - 1 - Block variable
# Outer value - 2 - Block variable
# Outer value - 3 - Block variable

Variable Lookup Order

ruby
global_var = "Global Variable"

class MyClass
  @@class_var = "Class Variable"
  
  def initialize
    @instance_var = "Instance Variable"
  end
  
  def method_with_local
    local_var = "Local Variable"
    
    # Variable lookup order: local -> instance -> class -> global
    puts local_var      # Local Variable
    puts @instance_var  # Instance Variable
    puts @@class_var    # Class Variable
    # puts global_var   # Error: Cannot directly access local variable outside method
  end
end

obj = MyClass.new
obj.method_with_local

🛡️ Variable Security and Best Practices

Variable Naming Conventions

ruby
# Good naming conventions
user_name = "Zhang San"           # Local variable
@user_email = "user@example.com"  # Instance variable
@@active_users = 0           # Class variable
$global_config = {}          # Global variable
MAX_LOGIN_ATTEMPTS = 3       # Constant

# Avoid naming
userName = "Li Si"            # camelCase (not recommended)
UserName = "Wang Wu"          # Looks like class name
user_name_ = "Zhao Liu"       # Trailing underscore (unclear)

Variable Initialization

ruby
class SafeVariableExample
  def initialize
    @initialized_var = "Initialized"
    @lazy_var = nil          # Explicitly initialize to nil
  end
  
  def get_lazy_var
    # Lazy initialization
    @lazy_var ||= expensive_computation
  end
  
  private
  
  def expensive_computation
    puts "Executing expensive computation..."
    "Computed result"
  end
end

Variable Validation

ruby
class ValidatedVariables
  def initialize(name, age)
    self.name = name  # Validate via setter method
    self.age = age
  end
  
  def name=(name)
    raise ArgumentError, "Name cannot be empty" if name.nil? || name.empty?
    @name = name
  end
  
  def age=(age)
    raise ArgumentError, "Age must be positive" unless age.is_a?(Integer) && age > 0
    @age = age
  end
  
  attr_reader :name, :age
end

# Usage example
begin
  person = ValidatedVariables.new("Zhang San", 25)
  puts "Created successfully: #{person.name}, #{person.age}"
  
  # person.age = -5  # Will raise ArgumentError
rescue ArgumentError => e
  puts "Error: #{e.message}"
end

🧪 Variable Practice Examples

Configuration Management Class

ruby
class ConfigManager
  @@configs = {}      # Class variable to store all configs
  @@default_config = {  # Default configuration
    timeout: 30,
    retries: 3,
    debug: false
  }
  
  def initialize(config_name)
    @config_name = config_name
    @@configs[@config_name] = @@default_config.dup
  end
  
  def set(key, value)
    @@configs[@config_name][key] = value
  end
  
  def get(key)
    @@configs[@config_name][key]
  end
  
  def self.get_config(config_name)
    @@configs[config_name]
  end
  
  def self.list_configs
    @@configs.keys
  end
end

# Use configuration manager
db_config = ConfigManager.new("database")
api_config = ConfigManager.new("api")

db_config.set(:host, "localhost")
db_config.set(:port, 5432)

api_config.set(:base_url, "https://api.example.com")
api_config.set(:timeout, 60)

puts db_config.get(:host)    # localhost
puts api_config.get(:timeout) # 60
puts ConfigManager.list_configs.inspect  # ["database", "api"]

Counter Class

ruby
class AdvancedCounter
  @@total_counters = 0    # Total counter count
  @@global_count = 0      # Global count
  
  def initialize(name)
    @name = name
    @count = 0
    @@total_counters += 1
  end
  
  def increment(step = 1)
    @count += step
    @@global_count += step
    self  # Return self for chaining
  end
  
  def decrement(step = 1)
    @count -= step
    @@global_count -= step
    self
  end
  
  def count
    @count
  end
  
  def name
    @name
  end
  
  def reset
    @@global_count -= @count
    @count = 0
    self
  end
  
  def self.total_counters
    @@total_counters
  end
  
  def self.global_count
    @@global_count
  end
  
  def self.reset_all
    @@global_count = 0
  end
  
  def info
    "#{@name}: #{@count}"
  end
end

# Use advanced counter
counter1 = AdvancedCounter.new("Counter 1")
counter2 = AdvancedCounter.new("Counter 2")

counter1.increment(5).increment(3)  # Chained calls
counter2.increment(2).decrement(1)

puts counter1.info  # Counter 1: 8
puts counter2.info  # Counter 2: 1
puts "Global count: #{AdvancedCounter.global_count}"  # Global count: 9
puts "Total counters: #{AdvancedCounter.total_counters}" # Total counters: 2

🎯 Variable Usage Best Practices

1. Minimize Scope

ruby
# Good practice: Define variables only when needed
def process_users(users)
  users.map do |user|
    # processed_user only used within map block
    processed_user = user.dup
    processed_user[:processed_at] = Time.now
    processed_user
  end
end

# Avoid: Defining variables too early
def bad_example(users)
  processed_user = nil  # Unnecessary early definition
  results = []
  
  users.each do |user|
    processed_user = user.dup
    processed_user[:processed_at] = Time.now
    results << processed_user
  end
  
  results
end

2. Use Descriptive Names

ruby
# Good naming
user_authentication_token = "abc123"
maximum_retry_attempts = 3
database_connection_timeout = 30

# Avoid vague naming
token = "abc123"
max_retries = 3
timeout = 30

3. Use Different Variable Types Appropriately

ruby
class UserService
  @@active_users = Set.new    # Class variable: track all active users
  DEFAULT_PAGE_SIZE = 20      # Constant: default page size
  
  def initialize(current_user)
    @current_user = current_user  # Instance variable: current user
  end
  
  def list_users(page = 1)
    page_size = DEFAULT_PAGE_SIZE  # Local variable: page size
    offset = (page - 1) * page_size
    
    # Query logic...
  end
end

📚 Next Steps

After mastering Ruby variables, it is recommended to continue learning:

Continue your Ruby learning journey!

Content is for learning and research only.