11

Can anyone expand upon, correct, or verify what I feel is happening when you pass arguments to a method in Ruby. Are any of these points wrong? Am I missing any pieces?

  • Everything in Ruby is an object.
  • Variables are references to objects
  • (When passing in a variable into a method): The parameter in the method that catches the variable is a local variable to that method. The parameter (local variable) now also has a reference to the same object.
  • I could alter the object (in place) and this alteration will hold when the method scope is exited. Any variables referencing this object outside the method scope will reflect that the object has been altered.
  • A new assignment to that parameter (local variable) does not change the original object, thus any references to it when the method leaves scope will remain unchanged.
  • If I am passing a variable into the method that references an Integer there is effectively no way that once that method exits I could have that variable referencing a new Integer.

Is there any way to have a method that takes as one of its parameters an Integer, does some stuff, and maybe as a side effect changes the value, having that change reflected once the method exits. Maybe I am just not thinking "the Ruby way".

Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338
slindsey3000
  • 4,053
  • 5
  • 36
  • 56
  • 2
    possible duplicate of [void foo(int &x) -> Ruby? Passing integers by reference?](http://stackoverflow.com/questions/2650634/void-fooint-x-ruby-passing-integers-by-reference) – derekerdmann Jan 06 '12 at 02:54

1 Answers1

22

Everything in Ruby is an object.

Close enough.

Variables are references to objects

No. A variable "names" an object: when a variable is evaluated, it evaluates to the object that it currently "names". Internally this is done by "storing a pointer" (or equivalent mechanism) to an object. (Although an implementation does not need to always use pointers: in Ruby MRI, for instance, Fixnum values actually exist without a real object.)

(When passing in a variable into a method): The parameter in the method that catches the variable is a local variable to that method. The parameter (local variable) now also has a reference to the same object.

No. See above. However, both variables now name (or "evaluate to") the same object. The parameters are passed internally using Call-by-Value -- that is, internally, the pointers to the objects are passed -- although Ruby has Call-by-Object-Sharing semantics, which is a term I try to promote as I find it succinctly describes the behavior.

I could alter the object (in place) and this alteration will hold when the method scope is exited. Any variables referencing this object outside the method scope will reflect that the object has been altered.

Yes, an object is itself. If you mutate that object, you mutate that object everywhere. But note: none of the variables are changed. Both the inside and the outside variables will still name (or "evaluate to") the same object.

A new assignment to that parameter (local variable) does not change the original object, thus any references to it when the method leaves scope will remain unchanged.

Correct. If you assign a different value to the local variable you make it, the local variable, name a different object. Ruby is not Call-by-Reference so the variable in the calling context is not altered.

If I am passing a variable into the method that references an Integer there is effectively no way that once that method exits I could have that variable referencing a new Integer?

Variables are never passed. Variables are evaluated to the objects they name and those objects are passed. Anyway, we know that:

  1. Ruby is not Call-by-Reference and;
  2. Integers (Fixnums) are immutable

Thus:

x = 1
y.foo(x)

can never change what x names, nor can it even change the contents of the object x names (because it's, well, immutable). Even if the object that x named was mutable, the method could not have changed what object x names: it could only have mutated the object that resulted from the evaluation of x.

Happy coding.


Now, The Ruby Way -- in my book -- would be to use a better return value that compassed all the new state, and let the caller put it where it needs to go :-)

Of course, mutable objects (including simple arrays) are also an option, but that's ick. And, if there is enough state that travels together, it might be a candidate for a separate class.


As a closing note: Ruby supports a concept of closures, so it is possible in a lexically-scoped manner:

x = 1; (lamb­da {|a| x = a}).c­all(2); x  // => 2

(This was shown for a simple lambda, but it is possible to design/craft a method to work similarly: in all the silly counter-examples like this, the outside variable itself needs to be known, however, as there is no way for the lambda/method of make an outside variable name a new object otherwise.)

  • Thank you for the fantastic answer! As to the "names" vs. "references" semantics, I don't understand the difference. A variable "names" an object... this gives it a "reference" to the object. Is it possible to "name" an object and not "reference" it? Is it possible to have a "reference" to an object and not "name" it? Are there examples you can point to that diagram this relationship, and maybe an example in another language where things are done differently under the hood? I just don't see how "naming" an object and "referencing" an object are different things. – slindsey3000 Jan 06 '12 at 19:20
  • I feel like the terms are interchangable and the "naming" an object implies "referencing" it, and "referencing" an object implies you have given it a "name". – slindsey3000 Jan 06 '12 at 19:24
  • 1
    @slindsey3000 I prefer the term "names" -- which is more prevalent in Python -- precisely because it *doesn't* use "reference" or any variant of. "[Pass by] Reference[es]" is such an overloaded set of words. But yes, colloquially they are many times treated the same. However, when merely using "names" and just defining it's relationship with the evaluation result of the variable, avoids conflicts when expanding the scope to talk about values/variables in many other languages. To say "int x = 1; // x reference 1" in C++, would be very wrong. The concept of "naming" is more general (and fringe). –  Jan 08 '12 at 08:36
  • 1
    @slindsey3000 That is, just as with the term "call-by-object-sharing", the semantics can be defined without defining or caring about *how* it's done, of which there are many different ways in many different languages. –  Jan 08 '12 at 08:43
  • however, this is the *exact* same semantics they call pass-by-value in Java – newacct Apr 23 '12 at 07:11
  • @newacct I consider the term slightly misleading and hold that the correct way to describe it in Java (esp. when talking to people familiar in other languages or not well versed in calling semantics), which *really is* call-by-value, is use the more wordy "call-by-value-of-the-reference". Once again, this is the *equivalent* implementation detail of call-by-object-sharing. Using the longer term helps avoid confusion with "making an object copy" that some people expect with call-by-value. (It really comes down to knowing *which* value is passed.) –  Apr 23 '12 at 17:23