Reputation: 368
After reading Is ruby pass by reference or value? I have learned a lot, but I am left with more questions than I had before reading it (which I suppose is good).
Consider the following example
def foo(bar)
bar = 'reference'
end
baz = 'value'
foo(baz)
puts "Ruby is pass-by-#{baz}"
Output
Ruby is pass-by-value
Here is my attempt to dissect how this works:
First, in the global scope baz
has the value value
.
Now foo
takes a parameter, whatever you pass into it, is on a local
level.
Therefore when we pass baz
in, there is ANOTHER baz
that is equal to reference
but this is on the local level, as a result, when we puts this on a global level it prints value
.
Now consider another example
def foo(bar)
bar.replace 'reference'
end
baz = 'value'
foo(baz)
puts "Ruby is pass-by-#{baz}"
Output
Ruby is pass-by-reference
If what I said above is true, does the .replace
method here change the global baz
? Am I interpreting this correctly? Please feel free to point out any mistakes in my attempts, I have no clue if im on the right track.
Thanks!
EDIT
More Magic
def my_foo(a_hash)
a_hash["test"]="reference"
end;
hash = {"test"=>"value"}
my_foo(hash)
puts "Ruby is pass-by-#{hash["test"]}"
Upvotes: 2
Views: 75
Reputation: 37617
Ruby is pass-by-value, but the values are references to objects.
In your first experiment, baz
is a reference to the string "value"
. bar
is initialized to a copy of baz
(that is, a copy of the reference) when you call foo
. You then overwrite bar
with a reference to the string "reference"
. Since bar
is a copy, overwriting it doesn't change baz
.
In your second experiment, again, baz
is a reference to the string "value"
and bar
is initialized to a copy of baz
when you call foo
. This time you don't overwrite bar
, but call a method on it. Although bar
is a copy of baz
, they refer to the same object (the string "value"
). Calling the method changes the state of that object. You then call to_s
on baz
(indirectly, by substituting it into "Ruby is pass-by-#{baz}"
), and to_s
returns the new state.
Your third experiment is a lot like the second. In the method, you change the state of the object referred to by the copy of the reference, then, outside the method, you read the new state back through the original reference.
Upvotes: 1
Reputation: 3053
In first case you use bar = 'reference'
which creates new object. In second one .replace
changes the object you apply it to. You can ensure this by .object_id
method. For example:
def foo_eql(bar)
bar = 'reference'
puts bar.object_id
bar
end
def foo_replace(bar)
bar.replace 'reference'
puts bar.object_id
bar
end
baz = 'value'
puts baz.object_id #Here you will get original baz object_id
res1 = foo_eql(baz) #Here you will get printed new object_id
res2 = foo_replace(baz) #Here you will get printed original baz object_id
puts "foo_eql: Ruby is pass-by-#{res1}"
=> foo_eql: Ruby is pass-by-reference
puts "foo_replace: Ruby is pass-by-#{res2}"
=> foo_replace: Ruby is pass-by-reference
So there is no magic at all. In your example with hashes you do not create new Hash object but modify existing one. But you can create new one with method like this:
def my_foo(a_hash)
a_hash = a_hash.merge({"test" => "reference"})
end
my_foo(hash)
puts "Ruby is pass-by-#{hash["test"]}"
Basically you pass the reference to an object in "value" way. For better understanding check this post and comments to it.
Upvotes: 0
Reputation: 2745
Maybe this will help to understand it:
x = 'ab'
x.object_id
=> 70287848748000 # a place in memory
x = 'cd'
x.object_id
=> 70287848695760 # other place in memory (other object)
x.replace('xy')
x.object_id
=> 70287848695760 # the same place in memory (the same object)
Upvotes: 1
Reputation: 3042
Very interesting thing.
Play with the object_id
s, you will see what ruby is doing bellow the scenes:
def foo(bar)
puts bar.object_id
bar = 'reference'
puts bar.object_id
end
baz = 'value'
puts baz.object_id
foo(baz)
Output
> baz = 'value'
=> "value"
> puts baz.object_id
70241392845040
> foo(baz)
70241392845040
70241392866940
After the local assign bar = 'reference'
, the local variable bar
will reference another object, so it won't change the original one.
It seems that in some cases it will make a dup
of your object.
Upvotes: 1
Reputation: 3830
It actually has nothing to do with passing parameters to methods. I extracted important parts from your examples:
baz = 'value'
bar = baz
bar = 'reference'
puts baz
bar = baz
bar.replace 'reference'
puts baz
You may think of variables as pointers. When you use =
, you make a variable point to something different, and the original value remains unchanged, and can be accessed through other variables that point at it. But when you use replace
, you change the content of the string the variable points at.
Upvotes: 0