Замораживание переменных в Ruby не работает

Я изучаю Ruby, и я просто нашел интересное поведение при использовании метода Object#freeze с переменными.

После того, как я заморожу переменную (либо Fixnum или Array), я все еще могу ее изменить! Это странно, поскольку, насколько мне известно, это не должно происходить, и TypeError должен быть поднят.

Здесь мой код:

test = 666
var = 90
#ok
var += 5

puts "var.frozen? #{var.frozen?}"    
var.freeze    
puts "var.frozen? #{var.frozen?}"

var = test
puts "var = #{var}"

То же самое для массива:

test = [666]
var = [90]
#ok
var += [5]

puts "var.frozen? #{var.frozen?}"    
var.freeze    
puts "var.frozen? #{var.frozen?}"

var = test
puts "var = #{var}"

Но когда я пытаюсь вытолкнуть что-то в массив после замораживания, он поднимает аранжировку, как и ожидалось:

test = [666]
var = [90]
#ok
var += [5]

puts "var.frozen? #{var.frozen?}"    
var.freeze    
puts "var.frozen? #{var.frozen?}"

var << test
puts "var = #{var}"

Может кто-нибудь объяснить мне эту проблему? Кажется странным.

Изменить Я использую Windows XP + Ruby 1.9.3-p429

Ответы

Ответ 1

Вы замораживаете объекты, а не переменные, т.е. вы не можете обновлять замороженный объект, но вы можете назначить новый объект той же переменной. Рассмотрим это:

a = [1,2,3]
a.freeze
a << 4
# RuntimeError: can't modify frozen Array

# `b` and `a` references the same frozen object
b = a
b << 4    
# RuntimeError: can't modify frozen Array

# You can replace the object referenced by `a` with an unfrozen one
a = [4, 5, 6]
a << 7
# => [4, 5, 6, 7]

В стороне: совершенно бесполезно замораживать Fixnum s, поскольку они являются неизменяемыми объектами.

Ответ 2

В Ruby переменные являются ссылками на объекты. Вы замерзаете объект, а не переменную.

Обратите внимание, что

a = [1, 2]
a.freeze
a += [3]

не является ошибкой, потому что + для массивов создает новый объект.

Ответ 3

Как упоминалось в двух других ответах, вы замораживаете объекты, а не переменные.

Я хотел бы добавить примечание к дочерним объектам, которые не заморожены, когда родитель замораживается. Это может укусить вас, если вы не обращаете внимания на то, что вы делаете, при экспонировании внутренних структур объекта:

class A
  attr_accessor :var
end

a = A.new
a.var = []
a.freeze
a.var = []   # this fails as expected
a.var << :a  # this works, raises no errors, and no warnings

Здесь вы можете прочитать о рациональности:

https://bugs.ruby-lang.org/issues/6037