Understanding passing by value vs. reference in this Ruby example - ruby

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"]}"

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.

Very interesting thing.
Play with the object_ids, 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.

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)

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.

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.

Related

Are Hashes in Ruby passed by reference? [duplicate]

#user.update_languages(params[:language][:language1],
params[:language][:language2],
params[:language][:language3])
lang_errors = #user.errors
logger.debug "--------------------LANG_ERRORS----------101-------------"
+ lang_errors.full_messages.inspect
if params[:user]
#user.state = params[:user][:state]
success = success & #user.save
end
logger.debug "--------------------LANG_ERRORS-------------102----------"
+ lang_errors.full_messages.inspect
if lang_errors.full_messages.empty?
#user object adds errors to the lang_errors variable in the update_lanugages method.
when I perform a save on the #user object I lose the errors that were initially stored in the lang_errors variable.
Though what I am attempting to do would be more of a hack (which does not seem to be working). I would like to understand why the variable values are washed out. I understand pass by reference so I would like to know how the value can be held in that variable without being washed out.
The other answerers are all correct, but a friend asked me to explain this to him and what it really boils down to is how Ruby handles variables, so I thought I would share some simple pictures / explanations I wrote for him (apologies for the length and probably some oversimplification):
Q1: What happens when you assign a new variable str to a value of 'foo'?
str = 'foo'
str.object_id # => 2000
A: A label called str is created that points at the object 'foo', which for the state of this Ruby interpreter happens to be at memory location 2000.
Q2: What happens when you assign the existing variable str to a new object using =?
str = 'bar'.tap{|b| puts "bar: #{b.object_id}"} # bar: 2002
str.object_id # => 2002
A: The label str now points to a different object.
Q3: What happens when you assign a new variable = to str?
str2 = str
str2.object_id # => 2002
A: A new label called str2 is created that points at the same object as str.
Q4: What happens if the object referenced by str and str2 gets changed?
str2.replace 'baz'
str2 # => 'baz'
str # => 'baz'
str.object_id # => 2002
str2.object_id # => 2002
A: Both labels still point at the same object, but that object itself has mutated (its contents have changed to be something else).
How does this relate to the original question?
It's basically the same as what happens in Q3/Q4; the method gets its own private copy of the variable / label (str2) that gets passed in to it (str). It can't change which object the label str points to, but it can change the contents of the object that they both reference to contain else:
str = 'foo'
def mutate(str2)
puts "str2: #{str2.object_id}"
str2.replace 'bar'
str2 = 'baz'
puts "str2: #{str2.object_id}"
end
str.object_id # => 2004
mutate(str) # str2: 2004, str2: 2006
str # => "bar"
str.object_id # => 2004
In traditional terminology, Ruby is strictly pass-by-value. But that's not really what you're asking here.
Ruby doesn't have any concept of a pure, non-reference value, so you certainly can't pass one to a method. Variables are always references to objects. In order to get an object that won't change out from under you, you need to dup or clone the object you're passed, thus giving an object that nobody else has a reference to. (Even this isn't bulletproof, though — both of the standard cloning methods do a shallow copy, so the instance variables of the clone still point to the same objects that the originals did. If the objects referenced by the ivars mutate, that will still show up in the copy, since it's referencing the same objects.)
Ruby uses "pass by object reference"
(Using Python's terminology.)
To say Ruby uses "pass by value" or "pass by reference" isn't really descriptive enough to be helpful. I think as most people know it these days, that terminology ("value" vs "reference") comes from C++.
In C++, "pass by value" means the function gets a copy of the variable and any changes to the copy don't change the original. That's true for objects too. If you pass an object variable by value then the whole object (including all of its members) get copied and any changes to the members don't change those members on the original object. (It's different if you pass a pointer by value but Ruby doesn't have pointers anyway, AFAIK.)
class A {
public:
int x;
};
void inc(A arg) {
arg.x++;
printf("in inc: %d\n", arg.x); // => 6
}
void inc(A* arg) {
arg->x++;
printf("in inc: %d\n", arg->x); // => 1
}
int main() {
A a;
a.x = 5;
inc(a);
printf("in main: %d\n", a.x); // => 5
A* b = new A;
b->x = 0;
inc(b);
printf("in main: %d\n", b->x); // => 1
return 0;
}
Output:
in inc: 6
in main: 5
in inc: 1
in main: 1
In C++, "pass by reference" means the function gets access to the original variable. It can assign a whole new literal integer and the original variable will then have that value too.
void replace(A &arg) {
A newA;
newA.x = 10;
arg = newA;
printf("in replace: %d\n", arg.x);
}
int main() {
A a;
a.x = 5;
replace(a);
printf("in main: %d\n", a.x);
return 0;
}
Output:
in replace: 10
in main: 10
Ruby uses pass by value (in the C++ sense) if the argument is not an object. But in Ruby everything is an object, so there really is no pass by value in the C++ sense in Ruby.
In Ruby, "pass by object reference" (to use Python's terminology) is used:
Inside the function, any of the object's members can have new values assigned to them and these changes will persist after the function returns.*
Inside the function, assigning a whole new object to the variable causes the variable to stop referencing the old object. But after the function returns, the original variable will still reference the old object.
Therefore Ruby does not use "pass by reference" in the C++ sense. If it did, then assigning a new object to a variable inside a function would cause the old object to be forgotten after the function returned.
class A
attr_accessor :x
end
def inc(arg)
arg.x += 1
puts arg.x
end
def replace(arg)
arg = A.new
arg.x = 3
puts arg.x
end
a = A.new
a.x = 1
puts a.x # 1
inc a # 2
puts a.x # 2
replace a # 3
puts a.x # 2
puts ''
def inc_var(arg)
arg += 1
puts arg
end
b = 1 # Even integers are objects in Ruby
puts b # 1
inc_var b # 2
puts b # 1
Output:
1
2
2
3
2
1
2
1
* This is why, in Ruby, if you want to modify an object inside a function but forget those changes when the function returns, then you must explicitly make a copy of the object before making your temporary changes to the copy.
Is Ruby pass by reference or by value?
Ruby is pass-by-value. Always. No exceptions. No ifs. No buts.
Here is a simple program which demonstrates that fact:
def foo(bar)
bar = 'reference'
end
baz = 'value'
foo(baz)
puts "Ruby is pass-by-#{baz}"
# Ruby is pass-by-value
Ruby is pass-by-value in a strict sense, BUT the values are references.
This could be called "pass-reference-by-value". This article has the best explanation I have read: http://robertheaton.com/2014/07/22/is-ruby-pass-by-reference-or-pass-by-value/
Pass-reference-by-value could briefly be explained as follows:
A function receives a reference to (and will access) the same object in memory as used by the caller. However, it does not receive the box that the caller is storing this object in; as in pass-value-by-value, the function provides its own box and creates a new variable for itself.
The resulting behavior is actually a combination of the classical definitions of pass-by-reference and pass-by-value.
There are already some great answers, but I want to post the definition of a pair of authorities on the subject, but also hoping someone might explain what said authorities Matz (creator of Ruby) and David Flanagan meant in their excellent O'Reilly book, The Ruby Programming Language.
[from 3.8.1: Object References]
When you pass an object to a method in Ruby, it is an object reference that is passed to the method. It is not the object itself, and it is not a reference to the reference to the object. Another way to say this is that method arguments are passed by value rather than by reference, but that the values passed are object references.
Because object references are passed to methods, methods can use those references to modify the underlying object. These modifications are then visible when the method returns.
This all makes sense to me until that last paragraph, and especially that last sentence. This is at best misleading, and at worse confounding. How, in any way, could modifications to that passed-by-value reference change the underlying object?
Is Ruby pass by reference or by value?
Ruby is pass-by-reference. Always. No exceptions. No ifs. No buts.
Here is a simple program which demonstrates that fact:
def foo(bar)
bar.object_id
end
baz = 'value'
puts "#{baz.object_id} Ruby is pass-by-reference #{foo(baz)} because object_id's (memory addresses) are always the same ;)"
=> 2279146940 Ruby is pass-by-reference 2279146940 because object_id's (memory addresses) are always the same ;)
def bar(babar)
babar.replace("reference")
end
bar(baz)
puts "some people don't realize it's reference because local assignment can take precedence, but it's clearly pass-by-#{baz}"
=> some people don't realize it's reference because local assignment can take precedence, but it's clearly pass-by-reference
Parameters are a copy of the original reference. So, you can change values, but cannot change the original reference.
Try this:--
1.object_id
#=> 3
2.object_id
#=> 5
a = 1
#=> 1
a.object_id
#=> 3
b = 2
#=> 2
b.object_id
#=> 5
identifier a contains object_id 3 for value object 1 and identifier b contains object_id 5 for value object 2.
Now do this:--
a.object_id = 5
#=> error
a = b
#value(object_id) at b copies itself as value(object_id) at a. value object 2 has object_id 5
#=> 2
a.object_id
#=> 5
Now, a and b both contain same object_id 5 which refers to value object 2.
So, Ruby variable contains object_ids to refer to value objects.
Doing following also gives error:--
c
#=> error
but doing this won't give error:--
5.object_id
#=> 11
c = 5
#=> value object 5 provides return type for variable c and saves 5.object_id i.e. 11 at c
#=> 5
c.object_id
#=> 11
a = c.object_id
#=> object_id of c as a value object changes value at a
#=> 11
11.object_id
#=> 23
a.object_id == 11.object_id
#=> true
a
#=> Value at a
#=> 11
Here identifier a returns value object 11 whose object id is 23 i.e. object_id 23 is at identifier a, Now we see an example by using method.
def foo(arg)
p arg
p arg.object_id
end
#=> nil
11.object_id
#=> 23
x = 11
#=> 11
x.object_id
#=> 23
foo(x)
#=> 11
#=> 23
arg in foo is assigned with return value of x.
It clearly shows that argument is passed by value 11, and value 11 being itself an object has unique object id 23.
Now see this also:--
def foo(arg)
p arg
p arg.object_id
arg = 12
p arg
p arg.object_id
end
#=> nil
11.object_id
#=> 23
x = 11
#=> 11
x.object_id
#=> 23
foo(x)
#=> 11
#=> 23
#=> 12
#=> 25
x
#=> 11
x.object_id
#=> 23
Here, identifier arg first contains object_id 23 to refer 11 and after internal assignment with value object 12, it contains object_id 25. But it does not change value referenced by identifier x used in calling method.
Hence, Ruby is pass by value and Ruby variables do not contain values but do contain reference to value object.
It should be noted that you do not have to even use the "replace" method to change the value original value. If you assign one of the hash values for a hash, you are changing the original value.
def my_foo(a_hash)
a_hash["test"]="reference"
end;
hash = {"test"=>"value"}
my_foo(hash)
puts "Ruby is pass-by-#{hash["test"]}"
Two references refer to same object as long as there is no reassignment.
Any updates in the same object won't make the references to new memory since it still is in same memory.
Here are few examples :
a = "first string"
b = a
b.upcase!
=> FIRST STRING
a
=> FIRST STRING
b = "second string"
a
=> FIRST STRING
hash = {first_sub_hash: {first_key: "first_value"}}
first_sub_hash = hash[:first_sub_hash]
first_sub_hash[:second_key] = "second_value"
hash
=> {first_sub_hash: {first_key: "first_value", second_key: "second_value"}}
def change(first_sub_hash)
first_sub_hash[:third_key] = "third_value"
end
change(first_sub_hash)
hash
=> {first_sub_hash: {first_key: "first_value", second_key: "second_value", third_key: "third_value"}}
Ruby is interpreted. Variables are references to data, but not the data itself. This facilitates using the same variable for data of different types.
Assignment of lhs = rhs then copies the reference on the rhs, not the data. This differs in other languages, such as C, where assignment does a data copy to lhs from rhs.
So for the function call, the variable passed, say x, is indeed copied into a local variable in the function, but x is a reference. There will then be two copies of the reference, both referencing the same data. One will be in the caller, one in the function.
Assignment in the function would then copy a new reference to the function's version of x. After this the caller's version of x remains unchanged. It is still a reference to the original data.
In contrast, using the .replace method on x will cause ruby to do a data copy. If replace is used before any new assignments then indeed the caller will see the data change in its version also.
Similarly, as long as the original reference is in tact for the passed in variable, the instance variables will be the same that the caller sees. Within the framework of an object, the instance variables always have the most up to date reference values, whether those are provided by the caller or set in the function the class was passed in to.
The 'call by value' or 'call by reference' is muddled here because of confusion over '=' In compiled languages '=' is a data copy. Here in this interpreted language '=' is a reference copy. In the example you have the reference passed in followed by a reference copy though '=' that clobbers the original passed in reference, and then people talking about it as though '=' were a data copy.
To be consistent with definitions we must keep with '.replace' as it is a data copy. From the perspective of '.replace' we see that this is indeed pass by reference. Furthermore, if we walk through in the debugger, we see references being passed in, as variables are references.
However if we must keep '=' as a frame of reference, then indeed we do get to see the passed in data up until an assignment, and then we don't get to see it anymore after assignment while the caller's data remains unchanged. At a behavioral level this is pass by value as long as we don't consider the passed in value to be composite - as we won't be able to keep part of it while changing the other part in a single assignment (as that assignment changes the reference and the original goes out of scope). There will also be a wart, in that instance variables in objects will be references, as are all variables. Hence we will be forced to talk about passing 'references by value' and have to use related locutions.
Lots of great answers diving into the theory of how Ruby's "pass-reference-by-value" works. But I learn and understand everything much better by example. Hopefully, this will be helpful.
def foo(bar)
puts "bar (#{bar}) entering foo with object_id #{bar.object_id}"
bar = "reference"
puts "bar (#{bar}) leaving foo with object_id #{bar.object_id}"
end
bar = "value"
puts "bar (#{bar}) before foo with object_id #{bar.object_id}"
foo(bar)
puts "bar (#{bar}) after foo with object_id #{bar.object_id}"
# Output
bar (value) before foo with object_id 60
bar (value) entering foo with object_id 60
bar (reference) leaving foo with object_id 80 # <-----
bar (value) after foo with object_id 60 # <-----
As you can see when we entered the method, our bar was still pointing to the string "value". But then we assigned a string object "reference" to bar, which has a new object_id. In this case bar inside of foo, has a different scope, and whatever we passed inside the method, is no longer accessed by bar as we re-assigned it and point it to a new place in memory that holds String "reference".
Now consider this same method. The only difference is what with do inside the method
def foo(bar)
puts "bar (#{bar}) entering foo with object_id #{bar.object_id}"
bar.replace "reference"
puts "bar (#{bar}) leaving foo with object_id #{bar.object_id}"
end
bar = "value"
puts "bar (#{bar}) before foo with object_id #{bar.object_id}"
foo(bar)
puts "bar (#{bar}) after foo with object_id #{bar.object_id}"
# Output
bar (value) before foo with object_id 60
bar (value) entering foo with object_id 60
bar (reference) leaving foo with object_id 60 # <-----
bar (reference) after foo with object_id 60 # <-----
Notice the difference? What we did here was: we modified the contents of the String object, that variable was pointing to. The scope of bar is still different inside of the method.
So be careful how you treat the variable passed into methods. And if you modify passed-in variables-in-place (gsub!, replace, etc), then indicate so in the name of the method with a bang !, like so "def foo!"
P.S.:
It's important to keep in mind that the "bar"s inside and outside of foo, are "different" "bar". Their scope is different. Inside the method, you could rename "bar" to "club" and the result would be the same.
I often see variables re-used inside and outside of methods, and while it's fine, it takes away from the readability of the code and is a code smell IMHO. I highly recommend not to do what I did in my example above :) and rather do this
def foo(fiz)
puts "fiz (#{fiz}) entering foo with object_id #{fiz.object_id}"
fiz = "reference"
puts "fiz (#{fiz}) leaving foo with object_id #{fiz.object_id}"
end
bar = "value"
puts "bar (#{bar}) before foo with object_id #{bar.object_id}"
foo(bar)
puts "bar (#{bar}) after foo with object_id #{bar.object_id}"
# Output
bar (value) before foo with object_id 60
fiz (value) entering foo with object_id 60
fiz (reference) leaving foo with object_id 80
bar (value) after foo with object_id 60
Yes but ....
Ruby passes a reference to an object and since everything in ruby is an object, then you could say it's pass by reference.
I don't agree with the postings here claiming it's pass by value, that seems like pedantic, symantic games to me.
However, in effect it "hides" the behaviour because most of the operations ruby provides "out of the box" - for example string operations, produce a copy of the object:
> astringobject = "lowercase"
> bstringobject = astringobject.upcase
> # bstringobject is a new object created by String.upcase
> puts astringobject
lowercase
> puts bstringobject
LOWERCASE
This means that much of the time, the original object is left unchanged giving the appearance that ruby is "pass by value".
Of course when designing your own classes, an understanding of the details of this behaviour is important for both functional behaviour, memory efficiency and performance.

Exclamation mark and assignment inside of a function

Can someone explain why this:
def do_something str
str = "bar"
end
​
str_main = "foo"
do_something str_main
​
puts str_main
displays foo?
And this:
def do_something str
str.capitalize!
end
​
str_main = "foo"
do_something str_main
​
puts str_main
displays Foo?
Because of the way Ruby passes arguments.
When the method is being called, you have two references, str_main and str, to the same object "foo".
In the first example, when you use str = "bar", you are just changing what the str reference points to. So now you have str_main -> "foo" and str -> "bar". Therefore, the original object is not changed.
In the second example, you didn't change the str reference and changed the string in place with a mutator method, thus changing the same object that str_main points to.
The exclamation mark or bang operator modifies the original value. It is a destructive method. For example, let's say you had a string
string = "hi";
If you call the upcase method, you will get the following
string.upcase
=> "HI"
However, if you call string again, you will get the initial value.
string
=> "hi"
Now, let's say you use the destructive method upcase!
string.upcase!
=> "HI"
Now, if you call string again, you will see that the value was mutated.
string
=> "HI"
In Ruby, references are passed by value. So, a reference to str_main is passed to method do_something, a copy of reference is present in variable str.
This, however, does not mean that value that is referred to by both variables also has been copied around - there is still a single copy of referred to value, which is the string defined in Main.
Hence, when you assign a new value to str, this does not alter the value of str_main. However, when you modify the value that is referred by str, its changes are visibble outside.
All ruby methods return the last thing evaluated. However, object assignment stays within the scope of the current code block. Assigning str_main to a new value within a method will not affect str_main, unless it was an instance variable (#str_main). Doing such allows you to reassign an object across scopes, or depths, of your program. This is why your first method outputs 'foo' instead of 'bar'.
Now, the second example. #capitalize is a method called on a string object. It returns a new String instance, where its value is original object capitalized.
string = 'foobar'
string.capitalize # => 'Foobar'
puts string # => 'foobar'
Notice how string is only modified temporarily, and when called again it is back to normal.
Many methods in ruby have counterparts ending in !. This convention is the same as: object = object.some_method. Instead of creating a new instance of an object, these methods edit the original object's value. In the case of #capitalize!, the string is capitalized and modified for future calls.
string = 'foo'
string.capitalize! # => 'Foo'
puts string # => 'Foo'
Back to your second example. Using the #capitalize! method within the scope of do_something allows you to modify the str_main object. In a similar way to making str_main an instance variable.

Ruby local_variable keeps referenced to #instance_variable

class Foo
def bar
#instance_variable = [['first']]
# make a duplicate object with the :dup method
local_variable=#instance_variable.dup
# They have different object_id
p #instance_variable.object_id
p local_variable.object_id
local_variable.each{|n|n.push('second')}
#instance_variable
end
end
f=Foo.new
p f.bar
=> 2000
=> 2002
=> [["first", "second"]]
It seems that the local_variable still references to the #instance_variable, although it is a different object. This behaviour is both with the push and unshift in the each block. With a normal assignment like local_variable='second', the result is as expected => [['first']]
I don't understand why local_variable.each{|n|n.push('second')} has an effect on the #instance_variable
Using Ruby-1.9.2p318
Both local_variable and #instance_variable have references to the same object, the inner array ['first']. And because it's a mutable Array, you can effect changes to one array through the other.
Object#dup in Ruby provides a shallow copy. In order to make a deep copy of an Array, you'd need to write some code (or find a library) that recursively walks the data structure, deep-cloning its pieces of mutable state.
The problem is you're not testing the right object. You say:
p #instance_variable.object_id
p local_variable.object_id
But that's not the object you're going to push onto. Try this instead:
p #instance_variable[0].object_id
p local_variable[0].object_id
They are the same object.
In other words, it is not the case that changing local_variable changes #instance_variable, but it just so happens that they both contain a reference to the same object, so obviously changing that object as pointed to by one changes that object as pointed to by the other.

Ruby: method inexplicably overwritten and set to nil

If I execute this ruby code:
def foo
100
end
p defined?(foo), foo
if false
foo = 200
end
p defined?(foo), foo
The output I get is:
"method"
100
"local-variable"
nil
Can someone explain to me why foo is set to nil after not executing the if? Is this expected behavior or a ruby bug?
Names on the left hand side of assignments get set to nil, even if the code can't be reached as in the if false case.
>> foo
NameError: undefined local variable or method `foo' for main:Object
...
>> if false
.. foo = 1
.. end #=> nil
>> foo #=> nil
When Ruby tries to resolve barewords, it first looks for local variables (there's a reference to that in the Pickaxe book, which I can't seem to find at the moment). Since you now have one called foo it displays nil. As Mischa noted, the method still can be called as foo().
This is what my pal and Ruby super-expert Josh Cheek had to say:
When Ruby sees the assignment, it initializes the variable in the current scope and sets it to nil. Since the assignment didn't get run, it didn't update the value of foo.
if statements don't change scope like blocks do. This is also the most important difference between
for x in xs
and
xs.each { |x| }
Here's another example:
a = 123 if a # => nil
a # => nil
We shouldn't be able to say if a because we never set a, but Ruby sees the a = 123 and initializes a, then gets to if a at which point a is nil
I'd consider it a quirk of the interpreter, really. Gary Bernhardt makes fun of it in wat (https://www.destroyallsoftware.com/talks/wat) with a = a
-Josh

Access variables programmatically by name in Ruby

I'm not entirely sure if this is possible in Ruby, but hopefully there's an easy way to do this. I want to declare a variable and later find out the name of the variable. That is, for this simple snippet:
foo = ["goo", "baz"]
How can I get the name of the array (here, "foo") back? If it is indeed possible, does this work on any variable (e.g., scalars, hashes, etc.)?
Edit: Here's what I'm basically trying to do. I'm writing a SOAP server that wraps around a class with three important variables, and the validation code is essentially this:
[foo, goo, bar].each { |param|
if param.class != Array
puts "param_name wasn't an Array. It was a/an #{param.class}"
return "Error: param_name wasn't an Array"
end
}
My question is then: Can I replace the instances of 'param_name' with foo, goo, or bar? These objects are all Arrays, so the answers I've received so far don't seem to work (with the exception of re-engineering the whole thing ala dbr's answer)
What if you turn your problem around? Instead of trying to get names from variables, get the variables from the names:
["foo", "goo", "bar"].each { |param_name|
param = eval(param_name)
if param.class != Array
puts "#{param_name} wasn't an Array. It was a/an #{param.class}"
return "Error: #{param_name} wasn't an Array"
end
}
If there were a chance of one the variables not being defined at all (as opposed to not being an array), you would want to add "rescue nil" to the end of the "param = ..." line to keep the eval from throwing an exception...
You need to re-architect your solution. Even if you could do it (you can't), the question simply doesn't have a reasonable answer.
Imagine a get_name method.
a = 1
get_name(a)
Everyone could probably agree this should return 'a'
b = a
get_name(b)
Should it return 'b', or 'a', or an array containing both?
[b,a].each do |arg|
get_name(arg)
end
Should it return 'arg', 'b', or 'a' ?
def do_stuff( arg )
get_name(arg)
do
do_stuff(b)
Should it return 'arg', 'b', or 'a', or maybe the array of all of them? Even if it did return an array, what would the order be and how would I know how to interpret the results?
The answer to all of the questions above is "It depends on the particular thing I want at the time." I'm not sure how you could solve that problem for Ruby.
It seems you are trying to solve a problem that has a far easier solution..
Why not just store the data in a hash? If you do..
data_container = {'foo' => ['goo', 'baz']}
..it is then utterly trivial to get the 'foo' name.
That said, you've not given any context to the problem, so there may be a reason you can't do this..
[edit] After clarification, I see the issue, but I don't think this is the problem.. With [foo, bar, bla], it's equivalent like saying ['content 1', 'content 2', 'etc']. The actual variables name is (or rather, should be) utterly irrelevant. If the name of the variable is important, that is exactly why hashes exist.
The problem isn't with iterating over [foo, bar] etc, it's the fundamental problem with how the SOAP server is returing the data, and/or how you're trying to use it.
The solution, I would say, is to either make the SOAP server return hashes, or, since you know there is always going to be three elements, can you not do something like..
{"foo" => foo, "goo" => goo, "bar"=>bar}.each do |param_name, param|
if param.class != Array
puts "#{param_name} wasn't an Array. It was a/an #{param.class}"
puts "Error: #{param_name} wasn't an Array"
end
end
OK, it DOES work in instance methods, too, and, based on your specific requirement (the one you put in the comment), you could do this:
local_variables.each do |var|
puts var if (eval(var).class != Fixnum)
end
Just replace Fixnum with your specific type checking.
I do not know of any way to get a local variable name. But, you can use the instance_variables method, this will return an array of all the instance variable names in the object.
Simple call:
object.instance_variables
or
self.instance_variables
to get an array of all instance variable names.
Building on joshmsmoore, something like this would probably do it:
# Returns the first instance variable whose value == x
# Returns nil if no name maps to the given value
def instance_variable_name_for(x)
self.instance_variables.find do |var|
x == self.instance_variable_get(var)
end
end
There's Kernel::local_variables, but I'm not sure that this will work for a method's local vars, and I don't know that you can manipulate it in such a way as to do what you wish to acheive.
Great question. I fully understand your motivation. Let me start by noting, that there are certain kinds of special objects, that, under certain circumstances, have knowledge of the variable, to which they have been assigned. These special objects are eg. Module instances, Class instances and Struct instances:
Dog = Class.new
Dog.name # Dog
The catch is, that this works only when the variable, to which the assignment is performed, is a constant. (We all know that Ruby constants are nothing more than emotionally sensitive variables.) Thus:
x = Module.new # creating an anonymous module
x.name #=> nil # the module does not know that it has been assigned to x
Animal = x # but will notice once we assign it to a constant
x.name #=> "Animal"
This behavior of objects being aware to which variables they have been assigned, is commonly called constant magic (because it is limited to constants). But this highly desirable constant magic only works for certain objects:
Rover = Dog.new
Rover.name #=> raises NoMethodError
Fortunately, I have written a gem y_support/name_magic, that takes care of this for you:
# first, gem install y_support
require 'y_support/name_magic'
class Cat
include NameMagic
end
The fact, that this only works with constants (ie. variables starting with a capital letter) is not such a big limitation. In fact, it gives you freedom to name or not to name your objects at will:
tmp = Cat.new # nameless kitty
tmp.name #=> nil
Josie = tmp # by assigning to a constant, we name the kitty Josie
tmp.name #=> :Josie
Unfortunately, this will not work with array literals, because they are internally constructed without using #new method, on which NameMagic relies. Therefore, to achieve what you want to, you will have to subclass Array:
require 'y_support/name_magic'
class MyArr < Array
include NameMagic
end
foo = MyArr.new ["goo", "baz"] # not named yet
foo.name #=> nil
Foo = foo # but assignment to a constant is noticed
foo.name #=> :Foo
# You can even list the instances
MyArr.instances #=> [["goo", "baz"]]
MyArr.instance_names #=> [:Foo]
# Get an instance by name:
MyArr.instance "Foo" #=> ["goo", "baz"]
MyArr.instance :Foo #=> ["goo", "baz"]
# Rename it:
Foo.name = "Quux"
Foo.name #=> :Quux
# Or forget the name again:
MyArr.forget :Quux
Foo.name #=> nil
# In addition, you can name the object upon creation even without assignment
u = MyArr.new [1, 2], name: :Pair
u.name #=> :Pair
v = MyArr.new [1, 2, 3], ɴ: :Trinity
v.name #=> :Trinity
I achieved the constant magic-imitating behavior by searching all the constants in all the namespaces of the current Ruby object space. This wastes a fraction of second, but since the search is performed only once, there is no performance penalty once the object figures out its name. In the future, Ruby core team has promised const_assigned hook.
You can't, you need to go back to the drawing board and re-engineer your solution.
Foo is only a location to hold a pointer to the data. The data has no knowledge of what points at it. In Smalltalk systems you could ask the VM for all pointers to an object, but that would only get you the object that contained the foo variable, not foo itself. There is no real way to reference a vaiable in Ruby. As mentioned by one answer you can stil place a tag in the data that references where it came from or such, but generally that is not a good apporach to most problems. You can use a hash to receive the values in the first place, or use a hash to pass to your loop so you know the argument name for validation purposes as in DBR's answer.
The closest thing to a real answer to you question is to use the Enumerable method each_with_index instead of each, thusly:
my_array = [foo, baz, bar]
my_array.each_with_index do |item, index|
if item.class != Array
puts "#{my_array[index]} wasn't an Array. It was a/an #{item.class}"
end
end
I removed the return statement from the block you were passing to each/each_with_index because it didn't do/mean anything. Each and each_with_index both return the array on which they were operating.
There's also something about scope in blocks worth noting here: if you've defined a variable outside of the block, it will be available within it. In other words, you could refer to foo, bar, and baz directly inside the block. The converse is not true: variables that you create for the first time inside the block will not be available outside of it.
Finally, the do/end syntax is preferred for multi-line blocks, but that's simply a matter of style, though it is universal in ruby code of any recent vintage.

Resources