@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

其他回答

试试这个:——

1.object_id
#=> 3

2.object_id
#=> 5

a = 1
#=> 1
a.object_id
#=> 3

b = 2
#=> 2
b.object_id
#=> 5

标识符a包含值对象1的object_id 3,标识符b包含值对象2的object_id 5。

现在这样做:——

a.object_id = 5
#=> error

a = b
#value(object_id) at b copies itself as value(object_id) at a. value object 2 has object_id 5
#=> 2

a.object_id 
#=> 5

现在,a和b都包含相同的object_id 5,它指向值对象2。 因此,Ruby变量包含object_ids来引用值对象。

执行以下操作也会给出错误:——

c
#=> error

但是这样做不会产生错误:——

5.object_id
#=> 11

c = 5
#=> value object 5 provides return type for variable c and saves 5.object_id i.e. 11 at c
#=> 5
c.object_id
#=> 11 

a = c.object_id
#=> object_id of c as a value object changes value at a
#=> 11
11.object_id
#=> 23
a.object_id == 11.object_id
#=> true

a
#=> Value at a
#=> 11

这里标识符a返回值对象11,其对象id为23,即object_id 23位于标识符a,现在我们看到一个使用method的例子。

def foo(arg)
  p arg
  p arg.object_id
end
#=> nil
11.object_id
#=> 23
x = 11
#=> 11
x.object_id
#=> 23
foo(x)
#=> 11
#=> 23

foo中的Arg被赋值为x。 它清楚地表明参数是由值11传递的,值11本身是一个对象,对象id为23。

现在再看这个:——

def foo(arg)
  p arg
  p arg.object_id
  arg = 12
  p arg
  p arg.object_id
end

#=> nil
11.object_id
#=> 23
x = 11
#=> 11
x.object_id
#=> 23
foo(x)
#=> 11
#=> 23
#=> 12
#=> 25
x
#=> 11
x.object_id
#=> 23

在这里,标识符arg首先包含object_id 23来引用11,在对值对象12进行内部赋值后,它包含object_id 25。但它不会改变在调用方法中使用的标识符x引用的值。

因此,Ruby是按值传递的,Ruby变量不包含值,但包含对值对象的引用。

Two references refer to same object as long as there is no reassignment. 

同一对象中的任何更新都不会引用到新的内存,因为它仍然在相同的内存中。 以下是一些例子:

    a = "first string"
    b = a



    b.upcase! 
    => FIRST STRING
    a
    => FIRST STRING

    b = "second string"


a
    => FIRST STRING
    hash = {first_sub_hash: {first_key: "first_value"}}
first_sub_hash = hash[:first_sub_hash]
first_sub_hash[:second_key] = "second_value"

    hash
    => {first_sub_hash: {first_key: "first_value", second_key: "second_value"}}

    def change(first_sub_hash)
    first_sub_hash[:third_key] = "third_value"
    end

    change(first_sub_hash)

    hash
    =>  {first_sub_hash: {first_key: "first_value", second_key: "second_value", third_key: "third_value"}}

Ruby是严格意义上的值传递,但是值是引用。

这可以称为“pass-reference-by-value”。这篇文章有我读过的最好的解释:http://robertheaton.com/2014/07/22/is-ruby-pass-by-reference-or-pass-by-value/

按值传递引用可以简单地解释如下:

函数接收(并将访问)调用者在内存中使用的同一对象的引用。但是,它不接收调用方存储该对象的盒子;与pass-value-by-value一样,函数提供自己的方框,并为自己创建一个新变量。

结果行为实际上是引用传递和值传递的经典定义的组合。

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

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

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

def foo(bar)
  bar.object_id
end

baz = 'value'

puts "#{baz.object_id} Ruby is pass-by-reference #{foo(baz)} because object_id's (memory addresses) are always the same ;)"

=> 2279146940 Ruby是引用传递的2279146940,因为object_id(内存地址)总是相同的;)

def bar(babar)
  babar.replace("reference")
end

bar(baz)

puts "some people don't realize it's reference because local assignment can take precedence, but it's clearly pass-by-#{baz}"

=>有些人没有意识到它是引用,因为局部赋值可以优先,但它显然是引用传递

是的,但是....

Ruby将引用传递给一个对象,因为Ruby中的所有东西都是对象,那么你可以说它是通过引用传递的。

我不同意这里的帖子声称它是通过价值,这对我来说似乎是迂腐的赛门铁克游戏。

然而,实际上它“隐藏”了行为,因为ruby提供的大多数操作都是“开箱即用”的——例如字符串操作,会生成对象的副本:

> astringobject = "lowercase"

> bstringobject = astringobject.upcase
> # bstringobject is a new object created by String.upcase

> puts astringobject
lowercase

> puts bstringobject
LOWERCASE

这意味着大多数时候,原始对象保持不变,给人一种ruby是“传递值”的感觉。

当然,在设计自己的类时,理解这种行为的细节对于功能性行为、内存效率和性能都很重要。