3

Okay, so Ruby is 'pass by value'. But how do you exactly define 'pass by reference' and 'pass by value' in Ruby? I've used this answer What's the difference between passing by reference vs. passing by value? and according to it, Ruby seems to be, well, a hybrid...

Technically, Ruby seems to be 'pass by value' with the difference that the value doesn't get COPIED when you pass it to a method. If we define "value" = the object, and "reference" = the reference variable that points to that object, will "pass by reference" makes sense if it's equivalent to "pass the reference variable that points to a specific object"? And then, once the "reference" is passed, the method doesn't make a COPY of the object, but actually has the ORIGINAL object itself (referenced by the variable) which can manipulate directly. Correct me if I'm wrong.

EDIT: I am AWARE of this question Is Ruby pass by reference or by value? but different people seem to have different definitions of pass by reference/value there.

Community
  • 1
  • 1
daremkd
  • 8,244
  • 6
  • 40
  • 66

3 Answers3

5

Ruby is pass-by-value, just like C, Java, Python, Smalltalk, ECMAScript and many others. C++ and C# are also pass-by-value by default, you have to use special annotations (& in C++, ref in C#), to use pass-by-reference.

The distinction is actually rather simple: if the the reference is passed, then the callee can modify it, otherwise it can't. In Ruby, the callee cannot modify the reference, ergo it is pass-by-value:

def is_ruby_pass_by_value?(foo)
  foo = 'No, Ruby is pass-by-reference.'
  return nil
end

bar = 'Yes, of course, Ruby *is* pass-by-value!'

is_ruby_pass_by_value?(bar)

p bar
# 'Yes, of course, Ruby *is* pass-by-value!'

As you can see, within the method is_ruby_pass_by_value?, the reference bar/foo is not being passed, otherwise the modification would be visible afterwards. bar is being passed by-value, i.e. the content of bar (the value contained in it) is being passed and not the reference itself.

Now, what is the value that is being passed? It is not the String object. Rather it is a pointer to that String object. More precisely: a copy of that pointer.

Now, there are two pointers to that String object. And that String object is mutable! So, if I follow one pointer (foo) and tell that String object to change itself, and then I follow the other pointer (bar) and ask it about its contents, then I will obviously see the changed contents. That's just the nature of shared mutable state, Ruby is not a purely functional, referentially transparent language:

def is_ruby_pass_by_value?(foo)
  foo.replace('More precisely, it is call-by-object-sharing!')
  foo = 'No, Ruby is pass-by-reference.'
  return nil
end

bar = 'Yes, of course, Ruby *is* pass-by-value!'

is_ruby_pass_by_value?(bar)

p bar
# 'More precisely, it is call-by-object-sharing!'

In fact, in Ruby, the value being held by variables and being passed as arguments is always a pointer. That's how almost all object-oriented languages work. Barbara Liskov called this special case of pass-by-value "call-by-object-sharing", it is also sometimes called "call-by-sharing" or "call-by-object".

Note, however, that the fact that the value being passed is a pointer, is completely irrelevant. Pass-by-value vs. pass-by-reference is about how arguments are being passed, not what the argument is. C is always pass-by-value, regardless of whether you are passing an int or a pointer. Pointers are still being passed by value. Likewise in Ruby, pointers are being passed by value. The differences between Ruby and C are a) that you can only pass pointers in Ruby, and b) that there is no special syntax indicating that you are passing a pointer.

[Note: most Ruby implementations will actually have optimizations in place for passing objects which are smaller than a pointer directly instead of passing a pointer to that object. However, they only do that for objects which are guaranteed by the language specification to be deeply immutable, so that it is impossible to observe the difference between passing a pointer to the value and passing the value directly. This is done, for example, for Fixnums, Symbols, Floats, nil, true and false.]

Here is an example in C#, that demonstrates the difference between pass-by-value (even if that value is a reference) and pass-by-reference:

class Program
{
    static void IsCSharpPassByValue(string[] foo, ref string baz)
    {
        foo[0] = "More precisely, for reference types it is call-by-object-sharing, which is a special case of pass-by-value.";
        foo = new string[] { "C# is not pass-by-reference." };

        baz = "It also supports pass-by-reference if explicitly requested.";
    }

    static void Main(string[] args)
    {
        var quux = new string[] { "Yes, of course, C# *is* pass-by-value!" };

        var grault = "This string will vanish because of pass-by-reference.";

        IsCSharpPassByValue(quux, ref grault);

        Console.WriteLine(quux[0]);
        // More precisely, for reference types it is call-by-object-sharing, which is a special case of pass-by-value.

        Console.WriteLine(grault);
        // It also supports pass-by-reference if explicitly requested.
    }
}
Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
  • @TonyHopkinson: I don't think this has anything to do with purism, linguistic descriptivism or any other kind of -ism. These are precise scientific terms that have precise scientific definitions, and either Ruby fits the definition (which it does for pass-by-value) or it doesn't (which is the case for pass-by-reference). C# is a good yardstick here, since it supports both pass-by-value and pass-by-reference for both reference types and value types. And Ruby behaves identical to pass-by-value of reference types in C#. So, if it behaves identical, why call it different (the opposite, even)? – Jörg W Mittag Nov 10 '13 at 13:03
  • @JörgWMittag: I love to read your opinions, but your response is entirely too long. Your `#is_ruby_pass_by_value?` _throws away_ the reference in the middle by re-assigning `foo`, and then pretends as if something was supposed to happen due to this. – Boris Stitnicky Nov 10 '13 at 13:07
  • @BorisStitnicky: it doesn't throw away the reference. It modifies it. If that modification were visible outside of `is_ruby_pass_by_value?`, then it would be pass-by-reference. That's the *definition* of pass-by-reference! I think I'll add an example in C#, as soon as Mono has finished compiling to illustrate the difference between pass-by-value and pass-by-reference. – Jörg W Mittag Nov 10 '13 at 13:09
  • Who says it has to fit the definition? Why does it have to fit the definition. Pass by value or reference is a simple concept in languages it pertains to, attempting to turn ruby into such a language so you can use the same definition, seems well bass ackwards.. – Tony Hopkinson Nov 10 '13 at 13:15
  • When you say: "Now, what is the value that is being passed? It is not the String object. Rather it is a pointer to that String object. More precisely: a copy of that pointer." >>> is the "pointer to that string object" => bar and the "copy of that pointer" => foo? – daremkd Nov 10 '13 at 13:23
  • @JörgWMittag: In Ruby, variables are not objects ([yet](http://bugs.ruby-lang.org/issues/8365)). What are you trying to do, to re-assign a local variable outside the `def` statement by an `=` statement inside? And if Ruby allowed you this, then it would be pass-by-reference? – Boris Stitnicky Nov 10 '13 at 13:23
  • @daremarkovic: In other words, you are saying that in some sense, a reference is also a value, right? – Boris Stitnicky Nov 10 '13 at 13:27
  • As I understood Jorg, a reference variable contains a value which is a pointer. That pointer points to an object in memory. So when you're passing a reference variable as an argument, a copy of the value of the pointer is passed. The confusing part to me in the first example is "foo = 'No, Ruby is pass-by-reference.'" he basically re-assigned foo to another object foo=String.new('No, Ruby is pass-by-reference.') so the value of the pointer got modified to point to another object. bar just got destroyed as a pointer then and the string object 'yes of..' can't be accessed anymore in that method. – daremkd Nov 10 '13 at 13:31
  • @daremarkovic: In Ruby, you call something similar to what you describe a "binding". I'm not really a big expert, but I edited my answer to make the point. – Boris Stitnicky Nov 10 '13 at 13:37
  • Um not sure if this has to do anything with bindings, it seems to be just the way value passing in Ruby works. I think Jorg referred to what a reference variable holds as a "pointer value" for the sake of clarification. It just seems he has some definition of 'pass by reference' I can't understand (not familiar with C# either). – daremkd Nov 10 '13 at 13:48
  • @daremarkovic: In Ruby, a binding is an explicit object (of `Binding` class), whereas a variable is not. You can pass bindings around like any other objects, but you cannot pass variables around in the same way. If you are missing variables being objects in Ruby, count me in. – Boris Stitnicky Nov 10 '13 at 13:51
  • @JörgWMittag I think I get what you're saying, basically, if Ruby was pass-by-reference, you could write a method that could modify where the argument reference variable points to i.e. change the "value" contained in the variable that tells the variable where to point to. That seems to be possible with some methods but not with the assignment operator. I think I get what you're saying, thanks a lot for the answer! – daremkd Nov 10 '13 at 13:59
  • @daremarkovic: Hey! But I just did write that method in Ruby, check my answer! You just cannot use `def` statement, you have to use `define_method` or similar block-accepting form! – Boris Stitnicky Nov 10 '13 at 14:02
  • Thanks for the answer Boris, gave you a thumbs up, however, I was looking for an answer that's more constrained to what I currently know. You introduce 3 or 4 different things I really don't know (like define_method, binding) and I really can't understand your answer, unlike Jorg's which is in the scope of my current Ruby knowledge. I was looking at how this pass by value/copy relates to usual def methods which are used in the majority of the code. Thank you for your answer once again. – daremkd Nov 10 '13 at 14:10
2

You can go purist on this and say ruby is a special case of "pass by the value of the reference" but it misses the point. Just think of everything in ruby being an object

foo(14) passes a reference to an integer object with a value of 14. There's some stuff that goes on under the covers, so you don't end up with 100's of 14 objects, but from an intent point of view most of the time you can just forget the concept.

Tony Hopkinson
  • 20,172
  • 3
  • 31
  • 39
2

Ruby is "pass by reference". The difference is as follows: If you pass by reference, you can do bad things to the original object:

x = [ "virgin" ]

def do_bad_things_to( arg )
  arg.clear << "bad things"
end

do_bad_things_to( x )

If you pass by value, you get the value of the original object and can work with it, but you cannot do bad things to the original object. You consume more memory, as the copy of the the original object's value also takes memory:

def pass_by_value( arg )
  arg.dup
end

y = [ "virgin" ]

do_bad_things_to( pass_by_value( y ) )

p x #=> [ "bad things" ]
p y #=> [ "virgin" ]

To immutable objects (numbers, symbols, true, false, nil...), one cannot do bad things by the virtue of their immutability. It is oft said, that in Ruby, they are passed by value, but in fact, the distinction makes little sense for them, just as it makes little sense to keep many copies of their internals in the memory.

UPDATE: There seems to be terminological contention as to what 'reference' means. In Ruby, Jörg Mittag's "pass by reference" is explicitly achieved by closures that close over local variables:

baz = "Jörg"
define_method :pass_by_Jorgs_reference_to_baz do baz = "Boris" end
pass_by_Jorgs_reference_to_baz
baz #=> "Boris"
Boris Stitnicky
  • 12,444
  • 5
  • 57
  • 74
  • That last code was really useful, I wasn't aware Ruby can do this. It seems that the current pass by reference/value terminology used in other languages is not really compatible in Ruby since there's a lot of misunderstandings of the actual definition of it in the Ruby lang. – daremkd Nov 10 '13 at 14:15
  • You don't pass *any* arguments to `pass_by_Jorgs_reference_to_baz`. You cannot make a determination about Ruby's argument passing semantics if you don't even pass any arguments. – Jörg W Mittag Nov 10 '13 at 14:32
  • @JörgWMittag: You are right about 1 thing: Ruby is not a pure functional language. The closure (the block) _closes over_ the local variables at the point where the block is defined, capturing `bar` in it. It also remembers the `Binding` object at that point. It can be actually evaluated in a different binding by methods like `instance_eval` and `Binding#eval`. `bar` is actually its silent argument, not explicitly mentioned between pipes. This is how half of the magic in Ruby is done. It took me some time to learn. – Boris Stitnicky Nov 10 '13 at 15:04