Ruby是按值传递的。总是。这是一个简单的程序,它证明了这一事实:
def foo(bar)
bar = 'reference'
end
baz = 'value'
foo(baz)
puts "Ruby is pass-by-#{baz}"
# Ruby is pass-by-value
您所看到的只是共享可变状态:如果一个对象是可变的,则它可以被变异,如果该对象通过多个引用可见,那么该变异将通过多个引用可见。就那么简单。不管你像我的朋友那样叫我“Jörg”还是像我妈妈那样叫我“儿子”,如果我剪了头发,你们两个都会看到。
如果您不希望您的对象发生变异,则需要使它们不可变。例如:
class Person
attr_reader :name
private
attr_writer :name
def initialize(name)
self.name = name
end
end
def get_name(obj)
obj.name = "Bob"
puts obj.name
end
jack = Person.new('Jack')
puts jack.name
# Jack
get_name(jack)
# NoMethodError: private method `name=' called for #<Person:0xdeadbeef081542 @name='Jack'>
puts jack.name
# Jack
现在,您的Person
对象不能再被其他对象更改。但是,请注意,您的对象引用的Person
对象显然仍然可以更改:
jack.name << ' the Ripper'
puts jack.name
# Jack the Ripper
jack.name.replace('Bob')
puts jack.name
# Bob
如果你想防止这种情况发生,你需要确保你的对象引用的所有Person
对象也是不可变的。例如像这样:
class Person
def initialize(name)
self.name = name.freeze
end
end
jack = Person.new('Jack)
jack.name << 'the Ripper'
# RuntimeError: can't modify frozen String
现在,您的Person
对象是真正不可变的。(至少像 Ruby 这样具有强大反射能力的语言一样“真实”。)
不幸的是,我们现在对其他人做了我们试图保护自己免受同样的事情:我们正在变异他们的对象!在Person#initialize
中,我们String
通过freeze
ing 来改变传入的那个。如果创建 的方法Person
想要对 做其他事情String
,他们会大吃一惊:
name = 'Jack'
jack = Person.new(name)
name << ' the Ripper'
# RuntimeError: can't modify frozen String
我们可以通过在ingString
之前复制第一个来解决这个问题:freeze
class Person
def initialize(name)
self.name = name.dup.freeze
end
end
name = 'Jack'
jack = Person.new(name)
name << ' the Ripper'
# => 'Jack the Ripper'