Reputation: 954
I want to set a variable if it's not already defined, so I write
if defined?(var).nil?
var = true
end
puts "[#{var}]"
This behaves as expected, it will print [true]
. But if I want to simplify the snippet and write:
var = true if defined?(var).nil?
puts "[#{var}]"
It will print []
.
What is the difference between these two snippets ?
Upvotes: 0
Views: 43
Reputation: 110685
Consider the following.
a #=> NameError (undefined local variable or method `a' for main:Object)
defined?(a)
#=> nil
a #=> NameError (undefined local variable or method `a' for main:Object)
As no local variable a
exists defined?
returns nil
but does not create the variable.
b = 1
c = defined?(b)
#=> "local-variable"
puts "cat" if c
cat
d = nil
e = defined?(nil)
#=> "nil"
e.nil?
#=> false
c
("local-variable"
) is truthy (logically true) because it is neither nil
or false
(the latter being falsy).
a #=> NameError (undefined local variable or method `a' for main:Object)
a = true if false
#=> nil
a #=> nil
This last result is an odd characteristic of Ruby. As soon as the parser sees the beginning of the assignment (a =
) it sets a
equal to nil
. I understand this is done for efficiency reasons.
Case 1
d = defined?(var1)
#=> nil
e = d.nil?
#=> true
var1 = true (since e #=> true)
var1
#=> true
Case 2
var2 =
# var2 is set equal to nil
d = defined?(var2)
#=> "nil"
e = d.nil?
#=> false
var2 = true if false
var2
#=> nil
puts "[#{nil}]"
[]
Upvotes: 1
Reputation: 369468
A local variable is defined from the point that the first assignment to it is parsed. Therefore, in your second snippet, the variable is defined at the point where you are calling defined?
(since Ruby is parsed like English, i.e. left-to-right, top-to-bottom), therefore the condition is always false, therefore the assignment never gets executed, therefore, the variable never gets initialized. Un-initialized local variables evaluate to nil
and nil.to_s
is the empty string.
Upvotes: 3