Project

General

Profile

Actions

Bug #13543

closed

local variable declaration

Added by eiko (eiko kokuma) over 5 years ago. Updated over 5 years ago.

Status:
Rejected
Priority:
Normal
Assignee:
-
Target version:
-
[ruby-core:80995]

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.


Related issues 1 (0 open1 closed)

Related to Ruby master - Feature #1141: assignment of variable in "right" if statement failsRejected02/11/2009Actions
Actions #1

Updated by eiko (eiko kokuma) over 5 years ago

  • Description updated (diff)
Actions #2

Updated by eiko (eiko kokuma) over 5 years ago

  • ruby -v set to 2.4.0rc1

Updated by Hanmac (Hans Mackowiak) over 5 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 5 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 5 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.

Actions #6

Updated by duerst (Martin Dürst) over 5 years ago

  • Related to Feature #1141: assignment of variable in "right" if statement fails added
Actions

Also available in: Atom PDF