Ruby: переменные @, @@ и $ — боль, память и просветление

В Ruby переменные с разными префиксами (@, @@, $) ведут себя по-разному, и понимание их областей видимости критично для написания чистого кода. В этой статье разберёмся, чем отличаются переменные экземпляра, класса и глобальные, как они влияют на архитектуру приложения и почему @@ лучше избегать. Особое внимание уделим подводным камням, которые могут возникнуть при работе с наследованием и глобальными состояниями.

Если ты когда-то на собеседовании перепутал @, @@ и $, знай: ты не один.
Сегодня мы расставим всё по полочкам (и смахнём пыль с @@).


🥸 Переменные в Ruby — в лицах

Вид переменной Синтаксис Где живёт Видимость
Локальная x Внутри метода/блока только там
Экземпляра @x Внутри объекта у каждого экземпляра
Класса @@x Внутри класса и его наследников у всего дерева
Глобальная $x Во всей программе везде (🤢)
Константа X В модуле или классе через ::

🧪 Примеры в бою

class Spaceship
  @@fleet_size = 0   # переменная класса

  def initialize(name)
    @name = name      # переменная экземпляра
    @@fleet_size += 1
  end

  def self.fleet_size
    @@fleet_size
  end

  def name
    @name
  end
end

s1 = Spaceship.new("Apollo")
s2 = Spaceship.new("Falcon")

puts s1.name             # => Apollo
puts Spaceship.fleet_size # => 2

🧬 Чем отличается @ от @@?

class Parent
  @@var = "from parent"
  @var  = "instance var of Parent"

  def self.vars
    [@@var, @var]
  end
end

class Child < Parent
  @@var = "from child"
  @var  = "instance var of Child"
end

puts Parent.vars.inspect # => ["from child", "instance var of Parent"]
puts Child.vars.inspect  # => ["from child", "instance var of Child"]

Вывод:

  • @@varодна на всё дерево: любые изменения — везде.
  • @varразные переменные: одна у Parent, другая у Child.

💀 Зачем избегать @@?

  1. Делится с наследниками — нарушает инкапсуляцию.
  2. Трудно отследить, где изменилась.
  3. Баги в проде → нытьё в Jira.

Совет: почти всегда лучше использовать @ на уровне class << self:

class MyClass
  class << self
    attr_accessor :my_var
  end
end

🗺 А что с $переменными?

$x = 42

class Demo
  def show
    puts $x
  end
end

Demo.new.show  # => 42
  • Глобально.
  • Бессмысленно.
  • Не надо так.

📌 Мнемоника:

  • @ — у объекта (@личное)
  • @@ — у всех потомков (двойной геморрой)
  • $ — всем пофиг, но видно везде

❗ Популярный подвох:

class A
  def self.set
    @@x = 1
  end
end

class B < A
end

B.set
puts A.class_variable_get(:@@x) # => 1

@@x завёлся в родителе, но установили его из наследника.


🔚 Вывод: Переменные с символами в Ruby — это не просто синтаксис, это целый стиль мышления. Если хочешь меньше боли — забудь про @@ и тем более про $. Будь современным рубистом — и пиши @ в нужном месте.

🗓 Дата публикации: 14.03.2023, но это не точно...

Ruby переменные область видимости экземпляр класс глобальные