Skip to content

Instantly share code, notes, and snippets.

@moxley
Created September 20, 2012 00:13
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save moxley/3753161 to your computer and use it in GitHub Desktop.
Save moxley/3753161 to your computer and use it in GitHub Desktop.
Weird Ruby Behavior
class MyClass
def value
"VALUE!"
end
def do_something
puts value # Outputs "VALUE!"
if false
value = nil # Should not execute
end
puts value # Outputs nothing
end
end
MyClass.new.do_something
@liuming
Copy link

liuming commented Sep 20, 2012

in your last puts, do this

puts value.inspect

@headius
Copy link

headius commented Sep 20, 2012

The first value is parsed as a method call (a vcall in MRI parser terms), so it calls your value method. Even though the value = nil is inside an "if false" block, from then on the parser sees it as a local variable. Since it is never actually initialized, it gets the default value of nil, and so puts prints a blank line for the third reference to "value".

@adzap
Copy link

adzap commented Sep 20, 2012

Local variable declarations are made regardless of whether the code is reached in the method. But existence of the declaration does depend on position in the flow of execution. And given that local variables shadow methods, unless you use an explicit receiver.

I think the rationale is that you could be just use any local variable that may have been declared in the flow of execution but you don't have to check its existence, because you can't really, unless you use meta programming.

@marksim
Copy link

marksim commented Sep 20, 2012

First call there is no definition of 'value' in the local scope, so it looks up to the instance, and finds the value method.

The interpreter then sees a definition of the 'value = nil' initializing 'value' as a local variable. The last puts outputs the value of 'value'. It's clearer what's happening if you change the 'value = nil' line to 'value = "value"' -- you still get

VALUE!
nil

@mboeh
Copy link

mboeh commented Sep 20, 2012

Local variable assignments in "keyword" blocks always result in that local variable being defined. If you did "for i in []" or "case...when false" it'd work, too. However, "[].each do" would not, nor would any other construct based around an explicit do/end block.

As I understand it, this is sort of an artifact of how local assignments inside "keyword" blocks are made inside the enclosing scope.

Shorter answer: if/case/while/etc. do not create a new scope for local variables, and local variables are defined in these constructs when they are parsed, not when they are executed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment