Bug #13543
closedlocal variable declaration
Description
The following code snippet generates a strange error:
def foo
@foo ||= "foo"
end
foo = foo.size
#=> undefined method 'size' for nil:NilClass (NoMethodError)
I expect the code to create a local variable foo
which is assigned the value 3
. I expect this because in assignment, the right size of =
is always resolved before being bound to the left side of =
. In my mind, foo
should refer to the foo()
method right up to the point that a value is assigned to the local variable which shadows it. It is confusing and unexpected to have a period of limbo in which foo()
is overshadowed by an unassigned local variable mid-declaration.
In other languages, declaration and initial assignments are atomic, so this code would throw errors:
In python:
foo = foo
# NameError: name 'foo' is not defined
In rust:
let foo = foo
// error: cannot find value 'foo' in this scope.
But in ruby, a variable can be instantly declared and assigned to itself:
foo = foo
#=> nil
This goes against the common motifs of programming in ruby and other languages and can lead to the confusing error mentioned above. Is there a reason local variables should be declared prior to resolving the assignment expression in ruby? I could find no documented reason for this design choice, leading me to believe it is a bug.
Updated by Hanmac (Hans Mackowiak) over 7 years ago
the problem: the parser does find: foo = foo.size
and detect that foo is a local variable, and does ignore it was defined as method before.
this can be shown in the code below:
def foo
@foo ||= "foo"
end
def test
p local_variables #=> [:foo]
foo = foo.size #=> NoMethodError
end
test
Updated by eiko (eiko kokuma) over 7 years ago
true, the parser detects the local variable early on, but it does not ignore the method until the point of assignment:
def foo
@foo ||= "foo"
end
p local_variables #=> [:foo]
p foo.size #=> 3
foo = foo.size #=> NoMethodError
so, despite the fact that local variables are determined before run-time, there's a mechanic in place to make sure they don't shadow methods until they are assigned. but why shadow the method mid-assignment when that's not common language design or seemingly useful as a design choice?
sorry if i'm in the wrong place for this. i had wanted to discuss this with ruby experts before raising the issue as a potential bug, but outside of stackoverflow, i don't know how best to communicate with people who know ruby well.
Updated by duerst (Martin Dürst) over 7 years ago
- Status changed from Open to Rejected
As far as I understand, the detection of variables (and as a consequence the shadowing of methods with the same name) occurs in lexical order. That means that because in
foo = foo.size
the variable name comes before the method call, the second foo
is already shadowed. Something similar also happens in e.g.
foo = 5 if foo.size > 10
Here, it's even more clear that the if is evaluated first (and the assignment maybe never), but still, the variable is lexically earlier, and so the method is shadowed. See also https://bugs.ruby-lang.org/issues/1141 and other bug reports linked from there.
Updated by duerst (Martin Dürst) over 7 years ago
- Related to Feature #1141: assignment of variable in "right" if statement fails added