ruby-on-rails ruby pass-by-reference

ruby on rails - ¿Es Ruby pasar por referencia o por valor?



ruby-on-rails pass-by-reference (12)

@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 objeto @user agrega errores a la variable lang_errors en el método update_lanugages . cuando realizo un guardado en el objeto @user , pierdo los errores que se almacenaron inicialmente en la variable lang_errors .

Aunque lo que estoy tratando de hacer sería más de un hack (que no parece estar funcionando). Me gustaría entender por qué se lavan los valores de las variables. Entiendo paso por referencia, por lo que me gustaría saber cómo se puede mantener el valor en esa variable sin ser eliminado.


¿Es Ruby pasar por referencia o por valor?

Ruby es el paso por referencia. Siempre. Sin excepciones. No ifs. Sin peros.

Aquí hay un programa simple que demuestra ese hecho:

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 se pasa por referencia 2279146940 porque las ID de objeto (direcciones de memoria) son siempre las mismas;)

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}"

=> algunas personas no se dan cuenta de que es una referencia porque la asignación local puede tener prioridad, pero es claramente una referencia paso a paso


¿Es Ruby pasar por referencia o por valor?

Ruby es el paso por valor. Siempre. Sin excepciones. No ifs. Sin peros.

Aquí hay un programa simple que demuestra ese hecho:

def foo(bar) bar = ''reference'' end baz = ''value'' foo(baz) puts "Ruby is pass-by-#{baz}" # Ruby is pass-by-value


Ruby usa "pasar por referencia de objeto"

(Usando la terminología de Python.)

Decir que Ruby usa "pasar por valor" o "pasar por referencia" no es lo suficientemente descriptivo como para ser útil. Creo que como la mayoría de la gente lo sabe en estos días, esa terminología ("valor" frente a "referencia") proviene de C ++.

En C ++, "pasar por valor" significa que la función obtiene una copia de la variable y cualquier cambio en la copia no cambia el original. Eso también es cierto para los objetos. Si pasa una variable de objeto por valor, el objeto completo (incluidos todos sus miembros) se copia y cualquier cambio en los miembros no cambia esos miembros en el objeto original. (Es diferente si pasas un puntero por valor, pero Ruby no tiene punteros, 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; }

Salida:

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

En C ++, "pasar por referencia" significa que la función obtiene acceso a la variable original. Puede asignar un entero literal completamente nuevo y la variable original tendrá ese valor también.

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; }

Salida:

in replace: 10 in main: 10

Ruby usa el paso por valor (en el sentido de C ++) si el argumento no es un objeto. Pero en Ruby todo es un objeto, por lo que realmente no hay un paso por valor en el sentido de C ++ en Ruby.

En Ruby, se usa "pasar por referencia de objeto" (para usar la terminología de Python):

  • Dentro de la función, cualquiera de los miembros del objeto puede tener nuevos valores asignados y estos cambios persistirán después de que la función regrese. *
  • Dentro de la función, la asignación de un objeto completamente nuevo a la variable hace que la variable deje de hacer referencia al objeto anterior. Pero una vez que la función regresa, la variable original seguirá haciendo referencia al objeto antiguo.

Por lo tanto, Ruby no usa "pasar por referencia" en el sentido de C ++. Si lo hiciera, la asignación de un nuevo objeto a una variable dentro de una función haría que el objeto antiguo se olvidara después de que la función regresara.

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

Salida:

1 2 2 3 2 1 2 1

* Esta es la razón por la que, en Ruby, si desea modificar un objeto dentro de una función pero olvidar esos cambios cuando la función vuelve, debe hacer una copia explícita del objeto antes de realizar cambios temporales en la copia.


Debe tenerse en cuenta que no es necesario utilizar el método "reemplazar" para cambiar el valor del valor original. Si asigna uno de los valores hash para un hash, está cambiando el valor original.

def my_foo(a_hash) a_hash["test"]="reference" end; hash = {"test"=>"value"} my_foo(hash) puts "Ruby is pass-by-#{hash["test"]}"


En terminología tradicional, Ruby es estrictamente paso por valor . Pero eso no es realmente lo que estás preguntando aquí.

Ruby no tiene ningún concepto de valor puro, que no sea de referencia, por lo que ciertamente no puedes pasarle uno a un método. Las variables son siempre referencias a objetos. Para obtener un objeto que no cambie de debajo de usted, necesita duplicar o clonar el objeto que se le pasa, dando así un objeto al que nadie más tiene una referencia. (Incluso esto no es a prueba de balas, sin embargo, ambos métodos de clonación estándar hacen una copia superficial, por lo que las variables de instancia del clon aún apuntan a los mismos objetos que hicieron los originales. Si los objetos a los que hace referencia los ivars mutan, eso aún aparece en la copia, ya que hace referencia a los mismos objetos.)


Los otros respondedores están todos en lo cierto, pero un amigo me pidió que le explicara esto y a lo que realmente se reduce es cómo Ruby maneja las variables, así que pensé que compartiría algunas fotos / explicaciones simples que escribí para él (disculpas por la longitud) y probablemente alguna simplificación excesiva):

P1: ¿Qué sucede cuando asigna una nueva variable str a un valor de ''foo'' ?

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

R: Se crea una etiqueta llamada str que apunta al objeto ''foo'' , que para el estado de este intérprete de Ruby se encuentra en la ubicación de memoria 2000 .

P2: ¿Qué sucede cuando asigna la variable str existente a un nuevo objeto usando = ?

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

R: La etiqueta str ahora apunta a un objeto diferente.

P3: ¿Qué sucede cuando asignas una nueva variable = a str ?

str2 = str str2.object_id # => 2002

R: Se crea una nueva etiqueta llamada str2 que apunta al mismo objeto que str .

P4: ¿Qué sucede si se modifica el objeto al que hacen referencia str y str2 ?

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

R: Ambas etiquetas siguen apuntando al mismo objeto, pero ese objeto ha mutado (su contenido ha cambiado para ser otra cosa).

¿Cómo se relaciona esto con la pregunta original?

Es básicamente lo mismo que sucede en Q3 / Q4; el método obtiene su propia copia privada de la variable / etiqueta ( str2 ) que se le pasa ( str ). No puede cambiar a qué objeto apunta la etiqueta, pero puede cambiar el contenido del objeto al que ambos hacen referencia para contener 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


Los parámetros son una copia de la referencia original. Por lo tanto, puede cambiar los valores, pero no puede cambiar la referencia original.


Prueba esto:--

1.object_id #=> 3 2.object_id #=> 5 a = 1 #=> 1 a.object_id #=> 3 b = 2 #=> 2 b.object_id #=> 5

el identificador a contiene object_id 3 para el objeto de valor 1 y el identificador b contiene object_id 5 para el objeto de valor 2.

Ahora hacer esto:--

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

Ahora, ayb contienen el mismo object_id 5 que se refiere al objeto de valor 2. Por lo tanto, la variable Ruby contiene object_ids para referirse a los objetos de valor.

Hacer lo siguiente también da error:

c #=> error

pero hacer esto no dará 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

Aquí, el identificador a devuelve el objeto de valor 11 cuya identificación de objeto es 23, es decir, object_id 23 está en el identificador a, Ahora vemos un ejemplo utilizando el método.

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

arg en foo se asigna con el valor de retorno de x. Muestra claramente que el argumento pasa el valor 11, y el valor 11 es un objeto con un ID de objeto único 23.

Ahora vea esto también: -

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

Aquí, el identificador argus primero contiene object_id 23 para referirse 11 y después de la asignación interna con el objeto de valor 12, contiene object_id 25. Pero no cambia el valor referenciado por el identificador x usado en el método de llamada.

Por lo tanto, Ruby se pasa por valor y las variables Ruby no contienen valores, pero sí contienen referencias a objetos de valor.


Ruby se interpreta. Las variables son referencias a datos, pero no los datos en sí. Esto facilita el uso de la misma variable para datos de diferentes tipos.

La asignación de lhs = rhs luego copia la referencia en los rhs, no en los datos. Esto difiere en otros idiomas, como C, donde la asignación hace una copia de datos a lhs desde rhs.

Entonces, para la llamada a la función, la variable pasada, digamos x, se copia en una variable local en la función, pero x es una referencia. Luego habrá dos copias de la referencia, ambas haciendo referencia a los mismos datos. Uno estará en la persona que llama, uno en la función.

La asignación en la función luego copiaría una nueva referencia a la versión de la función de x. Después de esto, la versión de x de la persona que llama permanece sin cambios. Todavía es una referencia a los datos originales.

En contraste, usar el método .replace en x hará que ruby ​​haga una copia de datos. Si se usa reemplazar antes de cualquier nueva asignación, entonces la persona que llama también verá el cambio de datos en su versión.

De manera similar, siempre que la referencia original esté intacta para la variable pasada, las variables de instancia serán las mismas que ve la persona que llama. En el marco de un objeto, las variables de instancia siempre tienen los valores de referencia más actualizados, ya sean proporcionados por el llamante o establecidos en la función a la que se pasó la clase.

La ''llamada por valor'' o ''llamada por referencia'' se confunde aquí debido a la confusión sobre ''='' En lenguajes compilados ''='' es una copia de datos. Aquí en este lenguaje interpretado ''='' es una copia de referencia. En el ejemplo, se ha pasado la referencia seguida de una copia de referencia, aunque ''='' indica que el original pasó la referencia, y luego la gente habla de ello como si ''='' fuera una copia de datos.

Para ser coherentes con las definiciones debemos mantener con ''.replace'' ya que es una copia de datos. Desde la perspectiva de ''.replace'', vemos que esto es efectivamente una referencia. Además, si recorremos el depurador, vemos que se pasan referencias, ya que las variables son referencias.

Sin embargo, si debemos mantener ''='' como marco de referencia, de hecho podemos ver los datos pasados ​​hasta una asignación, y luego ya no podemos verlos después de la asignación, mientras que los datos de la persona que llama permanecen sin cambios. A nivel de comportamiento, esto se pasa por valor siempre y cuando no consideremos que el valor pasado sea compuesto, ya que no podremos mantener parte de él mientras cambiamos la otra parte en una sola asignación (ya que esa asignación Cambia la referencia y el original queda fuera de alcance). También habrá una verruga, en ese caso las variables en los objetos serán referencias, al igual que todas las variables. Por lo tanto, nos veremos obligados a hablar sobre el paso de "referencias por valor" y tenemos que usar locuciones relacionadas.


Ruby se pasa por valor en sentido estricto, PERO los valores son referencias.

Esto podría llamarse " paso-referencia-por-valor ". Este artículo tiene la mejor explicación que he leído: robertheaton.com/2014/07/22/…

Paso-referencia-por-valor podría explicarse brevemente de la siguiente manera:

Una función recibe una referencia (y accederá) al mismo objeto en la memoria que usa la persona que llama. Sin embargo, no recibe la casilla en la que el llamante está almacenando este objeto; como en paso-valor-por-valor, la función proporciona su propia caja y crea una nueva variable para sí misma.

El comportamiento resultante es en realidad una combinación de las definiciones clásicas de paso por referencia y paso por valor.


Ya hay algunas respuestas geniales, pero quiero publicar la definición de un par de autoridades sobre el tema, pero también espero que alguien pueda explicar lo que dijeron las autoridades Matz (creador de Ruby) y David Flanagan en su excelente libro O''Reilly, El lenguaje de programación rubí .

[de 3.8.1: Referencias de objetos]

Cuando pasas un objeto a un método en Ruby, es una referencia de objeto que se pasa al método. No es el objeto en sí, y no es una referencia a la referencia al objeto. Otra forma de decir esto es que los argumentos del método se pasan por valor en lugar de por referencia , pero que los valores pasados ​​son referencias de objetos.

Debido a que las referencias de objetos se pasan a métodos, los métodos pueden usar esas referencias para modificar el objeto subyacente. Estas modificaciones son visibles cuando el método vuelve.

Todo esto tiene sentido para mí hasta el último párrafo, y especialmente la última oración. Esto es, en el mejor de los casos, engañoso y, en el peor, confuso. ¿Cómo, de alguna manera, podrían las modificaciones a esa referencia pasada por valor cambiar el objeto subyacente?


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

Cualquier actualización en el mismo objeto no hará las referencias a la nueva memoria ya que todavía está en la misma memoria. Aquí hay algunos ejemplos:

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"}}