Project

General

Profile

Bug #13837

Class attributes get overshadowed by local variables

Added by valerauko (Balint Erdos) about 1 month ago. Updated about 1 month ago.

Status:
Open
Priority:
Normal
Assignee:
-
Target version:
-
[ruby-core:82453]

Description

irb(main):001:0> RUBY_VERSION
=> "2.4.1"
irb(main):002:0> class Foo
irb(main):003:1>   attr_accessor :bar
irb(main):004:1>   def initialize
irb(main):005:2>     self.bar = 1
irb(main):006:2>   end
irb(main):007:1>   def baz
irb(main):008:2>     if bar.nil?
irb(main):009:3>       bar = 0
irb(main):010:3>     end
irb(main):011:2>   end
irb(main):012:1>   def fuga
irb(main):013:2>     bar = 0 if bar.nil?
irb(main):014:2>     [bar, self.bar]
irb(main):015:2>   end
irb(main):016:1>   def hoge
irb(main):017:2>     bar
irb(main):018:2>   end
irb(main):019:1> end
=> :hoge
irb(main):020:0> foo = Foo.new
=> #<Foo:0x00564abe333740 @bar=1>
irb(main):021:0> foo.bar
=> 1
irb(main):022:0> foo.hoge
=> 1
irb(main):023:0> foo.fuga
=> [0, 1]
irb(main):024:0> foo.bar
=> 1
irb(main):025:0> foo.baz
=> nil

Even though the bar.nil? in fuga should not be true, it gets overwritten by the local variable preceding the if and thus evaluates true.
I'd expect a behavior like below. It's really weird to get my class attributes overridden by a local variable that shouldn't even be evaluated.
~~~ ruby
1 if 2.nil?
=> nil
~~~

History

#1 [ruby-core:82454] Updated by Hanmac (Hans Mackowiak) about 1 month ago

ruby does parse the lines before exceute them.
so when ruby does read this:

bar = 0 if bar.nil?

it finds that you assign a local variable bar in this contect.
now it does always use the local variable instead of methods.

#2 [ruby-core:82455] Updated by shevegen (Robert A. Heiler) about 1 month ago

I think hanmac already gave the correct answer so there is no point in me adding
much to it. :)

Note that you could specifically point ruby to call the getter method in the
line hanmac pointed out, rather than using:

bar = 0 if bar.nil?

You could use:

bar = 0 if self.bar.nil?

And then the result would change.

I took the liberty to reformat your input, so that other people have
an easier time to copy/paste; the "irb" comments interfere there. I
also commented it a tiny bit to make it more clear.

Even the old pickaxe mentions that ruby may behave slightly unexpected
in regards to assignments and local variables when it comes to nil

class Foo

  attr_accessor :bar

  def initialize
    self.bar = 1 # Assigned 1 to @bar.
  end

  def hoge
    bar # Call reader method bar().
  end

  def fuga
    bar = 0 if bar.nil?
    [bar, self.bar]
  end

  def baz
    if bar.nil?
      bar = 0
    end
  end

end

foo = Foo.new
p foo.bar
p foo.hoge
p foo.fuga
p foo.bar
p foo.baz

Returns:

1
1
[0, 1]
1
nil

#3 [ruby-core:82456] Updated by valerauko (Balint Erdos) about 1 month ago

shevegen (Robert A. Heiler) wrote:

Even the old pickaxe mentions that ruby may behave slightly unexpected
in regards to assignments and local variables when it comes to nil

I still can't consider this correct behavior. Aren't the multi-line and the single-line ways of writing if supposed to be equivalent?

I took the liberty to reformat your input, so that other people have
an easier time to copy/paste; the "irb" comments interfere there. I
also commented it a tiny bit to make it more clear.

Thanks. Sorry about that.

Also available in: Atom PDF