15

我正在学习 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

4

3 回答 3

40

您冻结的是对象,而不是变量,也就是说,您不能更新冻结的对象,但可以将新对象分配给同一个变量。考虑一下:

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]

顺便说一句:冻结 s 是毫无用处Fixnum的,因为它们是不可变的对象。

于 2013-06-12T13:50:23.650 回答
18

在 Ruby 中,变量是对对象的引用。您冻结对象,而不是变量。

另请注意

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

不是错误,因为+数组创建了一个新对象。

于 2013-06-12T13:50:33.783 回答
13

正如其他两个答案中提到的,您冻结对象而不是变量。

我想在子对象上添加一个注释,当父对象被冻结时,这些对象不会被冻结。如果你在暴露一个对象的内部结构时不注意你在做什么,这可能会让你很难受:

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

于 2013-06-12T13:58:10.663 回答