Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby 'pass by value' clarification [duplicate]

Tags:

ruby

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.

like image 454
daremkd Avatar asked Apr 11 '26 12:04

daremkd


1 Answers

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.
    }
}
like image 118
Jörg W Mittag Avatar answered Apr 13 '26 12:04

Jörg W Mittag



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!