@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变量中的错误。

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


当前回答

试试这个:——

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变量不包含值,但包含对值对象的引用。

其他回答

其他的答案都是正确的,但是一个朋友让我向他解释这一点,归根结底就是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.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使用“对象引用传递”

(使用Python术语。)

说Ruby使用“按值传递”或“按引用传递”并不是真正的描述性足够有帮助。我想现在大多数人都知道,这个术语(“值”vs“引用”)来自c++。

在c++中,“传递值”意味着函数获得变量的副本,对副本的任何更改都不会改变原始变量。对象也是如此。如果你通过值传递一个对象变量,那么整个对象(包括它的所有成员)都会被复制,对成员的任何更改都不会改变原始对象上的那些成员。(如果你通过值传递一个指针,这是不同的,但Ruby没有指针,AFAIK。)

class A {
  public:
    int x;
};

void inc(A arg) {
  arg.x++;
  printf("in inc: %d\n", arg.x); // => 6
}

void inc(A* arg) {
  arg->x++;
  printf("in inc: %d\n", arg->x); // => 1
}

int main() {
  A a;
  a.x = 5;
  inc(a);
  printf("in main: %d\n", a.x); // => 5

  A* b = new A;
  b->x = 0;
  inc(b);
  printf("in main: %d\n", b->x); // => 1

  return 0;
}

输出:

in inc: 6
in main: 5
in inc: 1
in main: 1

在c++中,“引用传递”意味着函数可以访问原始变量。它可以分配一个全新的字面值整数,然后原始变量也会有这个值。

void replace(A &arg) {
  A newA;
  newA.x = 10;
  arg = newA;
  printf("in replace: %d\n", arg.x);
}

int main() {
  A a;
  a.x = 5;
  replace(a);
  printf("in main: %d\n", a.x);

  return 0;
}

输出:

in replace: 10
in main: 10

如果参数不是一个对象,Ruby使用按值传递(在c++的意义上)。但是在Ruby中,所有的东西都是一个对象,所以在Ruby中没有c++意义上的值传递。

在Ruby中,“通过对象引用传递”(使用Python的术语)被使用:

在函数内部,对象的任何成员都可以有新的值赋给他们,这些变化将在函数返回.*后持续存在 在函数内部,将一个全新的对象赋值给变量会导致变量停止引用旧对象。但是在函数返回后,原始变量仍然会引用旧对象。

因此Ruby不使用c++意义上的“引用传递”。如果存在,那么将一个新对象分配给函数内的变量将导致在函数返回后忘记旧对象。

class A
  attr_accessor :x
end

def inc(arg)
  arg.x += 1
  puts arg.x
end

def replace(arg)
  arg = A.new
  arg.x = 3
  puts arg.x
end

a = A.new
a.x = 1
puts a.x  # 1

inc a     # 2
puts a.x  # 2

replace a # 3
puts a.x  # 2

puts ''

def inc_var(arg)
  arg += 1
  puts arg
end

b = 1     # Even integers are objects in Ruby
puts b    # 1
inc_var b # 2
puts b    # 1

输出:

1
2
2
3
2

1
2
1

*这就是为什么在Ruby中,如果你想在函数中修改一个对象,但在函数返回时忘记这些更改,那么你必须在对副本进行临时更改之前显式地创建一个对象的副本。