@user.update_languages(params[:language][:language1], 
                       params[:language][:language2], 
                       params[:language][:language3])
lang_errors = @user.errors
logger.debug "--------------------LANG_ERRORS----------101-------------" 
                + lang_errors.full_messages.inspect

if params[:user]
  @user.state = params[:user][:state]
  success = success & @user.save
end
logger.debug "--------------------LANG_ERRORS-------------102----------" 
                + lang_errors.full_messages.inspect

if lang_errors.full_messages.empty?

@user对象将错误添加到update_languages方法中的lang_errors变量中。 当我在@user对象上执行保存时,我丢失了最初存储在lang_errors变量中的错误。

虽然我正在尝试做的更多的是一个黑客(似乎没有工作)。我想知道为什么变量值被洗掉了。我理解通过引用传递,所以我想知道值如何可以保存在那个变量中而不被洗掉。


当前回答

关于Ruby的“按值传递引用”如何工作的理论,有很多很棒的答案。但以身作则,我能更好地学习和理解一切。希望这对你们有帮助。

def foo(bar)
  puts "bar (#{bar}) entering foo with object_id #{bar.object_id}"
  bar =  "reference"
  puts "bar (#{bar}) leaving foo with object_id #{bar.object_id}"
end

bar = "value"
puts "bar (#{bar}) before foo with object_id #{bar.object_id}"
foo(bar)
puts "bar (#{bar}) after foo with object_id #{bar.object_id}"

# Output
bar (value) before foo with object_id 60
bar (value) entering foo with object_id 60
bar (reference) leaving foo with object_id 80 # <-----
bar (value) after foo with object_id 60 # <-----

正如你所看到的,当我们输入这个方法时,我们的工具条仍然指向字符串“value”。但是之后我们给bar分配了一个字符串对象“reference”,它有一个新的object_id。在这种情况下,foo内部的bar具有不同的作用域,无论我们在方法中传递什么,当我们重新分配它并将其指向内存中包含String "reference"的新位置时,bar都不再访问它。

现在考虑同样的方法。唯一的区别是在方法中使用do

def foo(bar)
  puts "bar (#{bar}) entering foo with object_id #{bar.object_id}"
  bar.replace "reference"
  puts "bar (#{bar}) leaving foo with object_id #{bar.object_id}"
end

bar = "value"
puts "bar (#{bar}) before foo with object_id #{bar.object_id}"
foo(bar)
puts "bar (#{bar}) after foo with object_id #{bar.object_id}"

# Output
bar (value) before foo with object_id 60
bar (value) entering foo with object_id 60
bar (reference) leaving foo with object_id 60 # <-----
bar (reference) after foo with object_id 60 # <-----

注意到区别了吗?我们在这里所做的是:我们修改了String对象的内容,变量所指向的对象。bar的范围在方法内部仍然是不同的。

所以要注意如何处理传递给方法的变量。如果修改传入的变量-in-place (gsub!, replace, etc),然后在方法的名称中以砰的一声表示so,比如“def foo!”

注:

重要的是要记住,foo的“bar”的内部和外部是“不同的”“bar”。它们的范围不同。在该方法中,您可以将“bar”重命名为“club”,结果将是相同的。

我经常看到变量在方法内部和外部被重用,虽然这很好,但它降低了代码的可读性,是一种代码气味。我强烈建议不要做我在上面例子中所做的:),而是这样做

def foo(fiz)
  puts "fiz (#{fiz}) entering foo with object_id #{fiz.object_id}"
  fiz =  "reference"
  puts "fiz (#{fiz}) leaving foo with object_id #{fiz.object_id}"
end

bar = "value"
puts "bar (#{bar}) before foo with object_id #{bar.object_id}"
foo(bar)
puts "bar (#{bar}) after foo with object_id #{bar.object_id}"

# Output
bar (value) before foo with object_id 60
fiz (value) entering foo with object_id 60
fiz (reference) leaving foo with object_id 80
bar (value) after foo with object_id 60

其他回答

其他的答案都是正确的,但是一个朋友让我向他解释这一点,归根结底就是Ruby是如何处理变量的,所以我想分享一些我为他写的简单的图片/解释(很抱歉篇幅太长,可能有些过于简化了):


Q1:当你将一个新变量str赋值为'foo'时会发生什么?

str = 'foo'
str.object_id # => 2000

答:一个名为str的标签被创建,指向对象'foo',对于这个Ruby解释器的状态来说,它恰好位于内存位置2000。


Q2:当你使用=将现有变量str赋值给一个新对象时会发生什么?

str = 'bar'.tap{|b| puts "bar: #{b.object_id}"} # bar: 2002
str.object_id # => 2002

A:标签str现在指向一个不同的对象。


Q3:当你分配一个新的变量= str会发生什么?

str2 = str
str2.object_id # => 2002

答:创建一个名为str2的新标签,指向与str相同的对象。


Q4:如果被str和str2引用的对象被改变了会发生什么?

str2.replace 'baz'
str2 # => 'baz'
str  # => 'baz'
str.object_id # => 2002
str2.object_id # => 2002

答:两个标签仍然指向同一个对象,但是对象本身已经发生了变化(它的内容已经改变为其他东西)。


这和最初的问题有什么关系?

这与第三季度和第四季度的情况基本相同;该方法获得传入给它的变量/ label (str2)的私有副本(str)。它不能改变标签str指向的对象,但它可以改变它们都引用的对象的内容,以包含else:

str = 'foo'

def mutate(str2)
  puts "str2: #{str2.object_id}"
  str2.replace 'bar'
  str2 = 'baz'
  puts "str2: #{str2.object_id}"
end

str.object_id # => 2004
mutate(str) # str2: 2004, str2: 2006
str # => "bar"
str.object_id # => 2004

在传统术语中,Ruby是严格的值传递。但这不是你真正想要的。

Ruby doesn't have any concept of a pure, non-reference value, so you certainly can't pass one to a method. Variables are always references to objects. In order to get an object that won't change out from under you, you need to dup or clone the object you're passed, thus giving an object that nobody else has a reference to. (Even this isn't bulletproof, though — both of the standard cloning methods do a shallow copy, so the instance variables of the clone still point to the same objects that the originals did. If the objects referenced by the ivars mutate, that will still show up in the copy, since it's referencing the same objects.)

Ruby是通过引用传递还是通过值传递?

Ruby是值传递。总是这样。没有例外。没有如果。少啰嗦

下面是一个简单的程序,说明了这一事实:

def foo(bar)
  bar = 'reference'
end

baz = 'value'

foo(baz)

puts "Ruby is pass-by-#{baz}"
# Ruby is pass-by-value

已经有了一些很好的答案,但我想在这里发布关于这个主题的一对权威的定义,但也希望有人能解释一下权威Matz (Ruby的创造者)和David Flanagan在他们的O'Reilly著作《Ruby编程语言》中所说的意思。

[from 3.8.1: Object References] When you pass an object to a method in Ruby, it is an object reference that is passed to the method. It is not the object itself, and it is not a reference to the reference to the object. Another way to say this is that method arguments are passed by value rather than by reference, but that the values passed are object references. Because object references are passed to methods, methods can use those references to modify the underlying object. These modifications are then visible when the method returns.

直到最后一段,尤其是最后一句,我才明白这一切。往好了说是误导,往坏了说是混淆。对值传递引用的修改如何以任何方式改变底层对象?

关于Ruby的“按值传递引用”如何工作的理论,有很多很棒的答案。但以身作则,我能更好地学习和理解一切。希望这对你们有帮助。

def foo(bar)
  puts "bar (#{bar}) entering foo with object_id #{bar.object_id}"
  bar =  "reference"
  puts "bar (#{bar}) leaving foo with object_id #{bar.object_id}"
end

bar = "value"
puts "bar (#{bar}) before foo with object_id #{bar.object_id}"
foo(bar)
puts "bar (#{bar}) after foo with object_id #{bar.object_id}"

# Output
bar (value) before foo with object_id 60
bar (value) entering foo with object_id 60
bar (reference) leaving foo with object_id 80 # <-----
bar (value) after foo with object_id 60 # <-----

正如你所看到的,当我们输入这个方法时,我们的工具条仍然指向字符串“value”。但是之后我们给bar分配了一个字符串对象“reference”,它有一个新的object_id。在这种情况下,foo内部的bar具有不同的作用域,无论我们在方法中传递什么,当我们重新分配它并将其指向内存中包含String "reference"的新位置时,bar都不再访问它。

现在考虑同样的方法。唯一的区别是在方法中使用do

def foo(bar)
  puts "bar (#{bar}) entering foo with object_id #{bar.object_id}"
  bar.replace "reference"
  puts "bar (#{bar}) leaving foo with object_id #{bar.object_id}"
end

bar = "value"
puts "bar (#{bar}) before foo with object_id #{bar.object_id}"
foo(bar)
puts "bar (#{bar}) after foo with object_id #{bar.object_id}"

# Output
bar (value) before foo with object_id 60
bar (value) entering foo with object_id 60
bar (reference) leaving foo with object_id 60 # <-----
bar (reference) after foo with object_id 60 # <-----

注意到区别了吗?我们在这里所做的是:我们修改了String对象的内容,变量所指向的对象。bar的范围在方法内部仍然是不同的。

所以要注意如何处理传递给方法的变量。如果修改传入的变量-in-place (gsub!, replace, etc),然后在方法的名称中以砰的一声表示so,比如“def foo!”

注:

重要的是要记住,foo的“bar”的内部和外部是“不同的”“bar”。它们的范围不同。在该方法中,您可以将“bar”重命名为“club”,结果将是相同的。

我经常看到变量在方法内部和外部被重用,虽然这很好,但它降低了代码的可读性,是一种代码气味。我强烈建议不要做我在上面例子中所做的:),而是这样做

def foo(fiz)
  puts "fiz (#{fiz}) entering foo with object_id #{fiz.object_id}"
  fiz =  "reference"
  puts "fiz (#{fiz}) leaving foo with object_id #{fiz.object_id}"
end

bar = "value"
puts "bar (#{bar}) before foo with object_id #{bar.object_id}"
foo(bar)
puts "bar (#{bar}) after foo with object_id #{bar.object_id}"

# Output
bar (value) before foo with object_id 60
fiz (value) entering foo with object_id 60
fiz (reference) leaving foo with object_id 80
bar (value) after foo with object_id 60