281 votes

Rubis passer par référence ou par valeur ?

@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 objet ajoute des erreurs à l' lang_errors variable dans l' update_lanugagesméthode. lorsque j'effectue une sauvegarde sur le @user objet que je perds les erreurs qui ont été initialement stockés dans l' lang_errors variable.

Si ce que je cherche à faire serait de plus d'un hack (ce qui ne semble pas fonctionner). J'aimerais comprendre pourquoi les valeurs de la variable sont délavées. Je comprends passage par référence, donc je voudrais savoir comment la valeur peut être tenu dans cette variable sans être lavé.

471voto

Abe Voelker Points 7306

Les autres answerers sont tout à fait correct, mais un ami m'a demandé de l'expliquer à lui et de ce que cela se résume à la façon dont Ruby traite des variables, alors j'ai pensé que je voudrais partager quelques photos / explications que j'ai écrit pour lui (mes excuses pour la longueur et probablement un peu trop simplifié):


Q1: Qu'advient-il lorsque vous affectez une nouvelle variable str valeur 'foo'?

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

enter image description here

Un: Un label" str est créé que des points à l'objet 'foo', ce qui, pour l'état de cet interpréteur Ruby arrive à être à l'emplacement de la mémoire 2000.


Q2: Qu'advient-il lorsque vous affectez à la variable existante str d'un nouvel objet à l'aide d' =?

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

enter image description here

Un: L'étiquette str maintenant pointe vers un objet différent.


Q3: Qu'advient-il lorsque vous affectez une nouvelle variable = de str?

str2 = str
str2.object_id # => 2002

enter image description here

A: Un nouveau label" str2 est créé qui pointe vers le même objet que str.


Q4: Qu'advient-il si l'objet référencé par str et str2 est changé?

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

enter image description here

R: les Deux étiquettes toujours au même objet, mais l'objet en lui-même a muté (son contenu ont été modifiés pour être autre chose).


Comment est-ce lié à la question de départ?

C'est essentiellement le même que ce qui se passe en Q3/Q4; la méthode obtient sa propre copie privée de la variable / étiquette (str2) qui est transmis (str). Il ne peut pas changer l'objet sur lequel l'étiquette str de points à, mais il peut changer le contenu de l'objet qu'ils ont tous deux référence à contenir autre chose:

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

259voto

Chuck Points 138930

Dans la terminologie traditionnelle, Ruby est strictement passé par valeur. Mais ce n'est pas vraiment ce que vous demandez ici.

Ruby n'a pas le concept de la pure, de la non-valeur de référence, alors vous avez certainement ne pouvez pas passer à une méthode. Les Variables sont toujours des références à des objets. Afin d'obtenir un objet qui ne change pas de sous, vous devez vous dup ou clone de l'objet que vous êtes passé, et donc de donner un objet que personne d'autre n'a une référence à l'. (Même ce n'est pas l'épreuve des balles, même si - à la fois de la norme méthodes de clonage faire une copie superficielle, de sorte que les variables d'instance de la clone pointent toujours vers les mêmes objets que les originaux n'avaient. Si les objets référencés par le ivars muter, ce sera toujours apparaître dans la copie, puisque c'est de référencement sur les mêmes objets.)

40voto

Jörg W Mittag Points 153275

Est-ce que Ruby passe par référence ou par valeur?

Ruby est une valeur de passage. Toujours. Aucune exception. Non si Pas de mais.

Voici un programme simple qui démontre ce fait:

 def foo(bar)
  bar = 'reference'
end

baz = 'value'

foo(baz)

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

17voto

Dominick Points 58

Il y a déjà quelques grandes réponses, mais je veux mettre la définition d'une paire des autorités sur le sujet, mais aussi en espérant que quelqu'un peut expliquer ce qu'a indiqué que les autorités Matz (le créateur de Ruby) et David Flanagan signifiait dans leur excellent O'Reilly livre, Le Langage de Programmation Ruby.

[à partir de 3.8.1: Références de l'Objet]

Lorsque vous transmettez un objet à une méthode de Ruby, c'est un objet de référence qui est passé à la méthode. Il n'est pas l'objet lui-même, et il n'est pas une référence à la référence à l'objet. Une autre façon de dire que c'est la méthode, les arguments sont passés par valeur plutôt que par référence, mais que les valeurs transmises sont des références d'objet.

Parce que les références de l'objet sont passés à des méthodes, des méthodes d'utilisation de ces références à modifier l'objet sous-jacent. Ces modifications sont ensuite visibles quand le retour de la méthode.

Tout cela fait sens pour moi jusqu'à ce que le dernier paragraphe, et surtout la dernière phrase. C'est, au mieux, trompeuses, et au pire de la confusion. Comment, en quelque sorte, pourrait modifications qui sont passés par valeur de référence de changement de l'objet sous-jacent?

15voto

Brett Allred Points 1993

Est Ruby passer par référence ou par valeur?

Ruby est passé par référence. Toujours. Pas d'exceptions. Pas de fi. Pas de mais.

Voici un programme simple qui démontre que le fait:

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 est passé par référence 2279146940 parce que object_id (adresses de mémoire) sont toujours les mêmes ;)

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

=> certaines personnes ne réalisent pas, c'est la référence, puisque l'attribution peut prendre la priorité, mais c'est clairement passé par référence

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X