Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby: How do to initialize the value of a block variable

Tags:

ruby

I am studying Ruby, and typed a piece of code to experiment with block-scoped variables:

x = 10
3.times do |i; x|
  x = x + 1
  puts("inside block is #{x}")
end
puts("outside block is #{x}")

I would expect the x block variable to automatically capture the value of the x global variable, and then x inside the block to be 11 every time, and x outside the block to be protected, and stay at 10. This "shielding" action is what is described in many Ruby tutorials found throughout the web.

But instead, the script fails, telling me that x is nil and that it doesn't have a + function. In other words, the x block variable hasn't been initialized with a value.

The exact code above is in a file called delete_me.rb, and ran with: ruby delete_me.rb

When I run the script, I get the following error:

delete_me.rb:3:in `block in <main>': undefined method `+' for nil:NilClass (NoMethodError)
    from delete_me.rb:2:in `times'
    from delete_me.rb:2:in `<main>'

How do I initialize the value of a block variable in Ruby?

like image 611
ouni Avatar asked Jun 28 '26 01:06

ouni


1 Answers

I would expect the x block variable to automatically capture the value of the x global variable,

That is not how Ruby works, and there is no reason to expect it to behave that way. An inner variable never takes its value from a variable it is shadowing, in any language that I know of. That would be a horribly designed language, where unrelated outer context could be changed or introduced that breaks an inner scope in completely unanticipated ways. The entire purpose of scope is to prevent this kind of thing.

x is nil, because it's a newly introduced variable that you didn't assign a value to.

How do I initialize the value of a block variable in Ruby?

You can check if it is nil, and then assign a value to it:

x = 10
3.times do |i; x|
  x ||= 0
  x = x + 1
  puts("inside block is #{x}")
end
puts("outside block is #{x}")

Note that this loop prints "inside block is 1", three times. You're making a new x and setting it to 0 each time the block is invoked. If you want to accumulate state while you iterate, this is the wrong way to go about it. You either want to not shadow the outer x, or use a different Enumerable method like inject or each.with_object.

like image 55
meagar Avatar answered Jun 30 '26 00:06

meagar



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!