jrhee17
jrhee17

Reputation: 1167

Ruby string pass by reference function parameter

Ruby noob here

I understand ruby does pass by reference for function parameters

However, I am getting the feeling this is slightly different from conventional c/c++ style pass by reference

Sample code:

def test1(str)
    str += ' World!'
end

def test2(str)
    str << ' World!'
end

str = 'Hello'

test1(str)
p str # Hello

test2(str)
p str # Hello World!

I would expect test1 to also return Hello World! if I were using references in c/c++.

This is simply out of curiosity -- any explanations would be appreciated

Upvotes: 6

Views: 5619

Answers (5)

sfy
sfy

Reputation: 3238

STRING IS REFERENCED, in ruby except value like numbers, true, false, nil, others are referenced.

a = "hello"
b = a
a.replace("Hola")
p a # Hola
p b # Hola

You would wanna add the magic comment at the beginning.:

# frozen_string_literal: true
a = "hello"
b = a
a.replace("Hola") #  can't modify frozen String: "hello" (FrozenError)

Upvotes: 0

J&#246;rg W Mittag
J&#246;rg W Mittag

Reputation: 369556

I understand ruby does pass by reference for function parameters

Ruby is strictly pass-by-value, always. There is no pass-by-reference in Ruby, ever.

This is simply out of curiosity -- any explanations would be appreciated

The simple explanation for why your code snippet doesn't show the result you would expect for pass-by-reference is that Ruby isn't pass-by-reference. It is pass-by-value, and your code snippet proves that.

Here is a small snippet that demonstrates that Ruby is, in fact, pass-by-value and not pass-by-reference:

#!/usr/bin/env ruby

def is_ruby_pass_by_value?(foo)
  foo << <<~HERE
    More precisely, it is call-by-object-sharing!
    Call-by-object-sharing is a special case of pass-by-value, 
    where the value is always an immutable pointer to a (potentially mutable) value.
  HERE
  foo = 'No, Ruby is pass-by-reference.'
  return
end

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

is_ruby_pass_by_value?(bar)

puts bar
# Yes, of course, Ruby *is* pass-by-value!,
# More precisely, it is call-by-object-sharing!
# Call-by-object-sharing is a special case of pass-by-value, 
# where the value is always an immutable pointer to a (potentially mutable) value.

Ruby does however allow mutation of objects, it is not a purely functional language like Haskell or Clean.

Upvotes: 4

peter
peter

Reputation: 42207

Look at the following adaption of your test, by showing the object_id of your object you can easily see if it is the same or not. Test1 returns another String object because of the += concatenation but it is not used afterward. This looks like passed by reference but in reality it is the value of the pointer to the object that is passed. The best explenation I could find for this is here , the author calls it pass-reference-by-value

def test1(str)
    p ["in test1 before", str.object_id]
  str += ' World!'
  p ["in test1 after", str.object_id]
  str
end

def test2(str)
    p ["in test2", str.object_id]
  str << ' World!'
end

str = 'Hello'
p ["in main", str.object_id]

test1(str)
p str # Hello
p ["after test1", str.object_id]

test2(str)
p str 
p ["after test2", str.object_id]

Gives

["in main", 12363600]
["in test1 before", 12363600] # the same object, so pointer to object passed by value    
["in test1 after", 12362976] # returns a new object, the old is unchanged
"Hello"
["after test1", 12363600] # idem
["in test2", 12363600]
"Hello World!"
["after test2", 12363600]
# still the same object

Upvotes: 0

Abdoo Dev
Abdoo Dev

Reputation: 1276

In the first case a new object was created when you did str += ' World!'

str = "Hello"
=> "Hello"
str.object_id
=> 69867706917360
str += " World"
=> "Hello World"
str.object_id
=> 69867706885680

str = "Hello"
=> "Hello"
str.object_id
=> 69867706856200
str << " World"
=> "Hello World"
str.object_id
=> 69867706856200

str = "Hello"
=> "Hello"
str.object_id
=> 69867706786780
str.freeze
=> "Hello"
str << " World"
RuntimeError: can't modify frozen String
str += " World"
=> "Hello World"

"<<" is a Binary Left Shift Operator. The left operands value is moved left by the number of bits specified by the right operand.

So "<<" doesn't create a new string, str.contact("World") doesn't create a new string as well. The method test1 doesn't have to do anything with the returned result , you can try this method :

def test1(str)
    str.concat(' World!')
end

Upvotes: 1

petrch
petrch

Reputation: 532

def test1(str)
    str += ' World!'
end

operator += is a syntactic sugar in ruby. Expression a += b translates to a = a + b. Operator + applied on String instance creates new String, which is a concatenation of the two arguments. That is why str is not modified in the first case.

Also I'd like to correct your statement:

I understand ruby does pass by reference for function parameters

Actually ruby passes by reference every parameter except the "value types" - ie. values nil, true, false and instances of class Fixnum

Upvotes: -2

Related Questions