3

How to pass an instance variable as an argument to a block?

This doesn't work in Ruby 1.9 (formal argument cannot be an instance variable).

awesome do |foo, @bar|
  puts 'yay'
end

2 Answers 2

4

Ruby 1.9 makes block parameters local to the block. This also means that block parameters can no longer be global or instance variables. You can use closures for your purposes:

@foo = 10
1.times do |i|
  puts @foo
end
# => "10"

UPDATE Instead of using instance variables, local variable can help you:

foo = 10
1.times do |i|
  puts foo
end
# => "10"

In such cases, you won't get problems related to code execution inside different context.

Sign up to request clarification or add additional context in comments.

2 Comments

I can't do that. That's why I was confused! Take a look at my new question.
Ok, got it. I've updated my post and answered on your question.
2

There are 3 cases here which could be relevant:

  • Instance variable of the definer of the block that references the instance variable (in the example the usage of awesome2.
  • Instance variable of the definer of the block that gives the instance variable as argument to the receiver (no chance to give it as argument to the block). This is usage of awesome.
  • Instance variable of the user of the block that gives the instance variable as argument to the block. This is usage of awesome3.

So lets try to implement both examples to see them. The example is lengthy, the relevant lines are:

  • awesome gets the instance variable as argument, which is then used by the receiver in the call of the block
  • awesome2 gets no instance variable, it is bound by Sender#use_in_block. No chance for the receiver to change that binding.
  • awesome3 gets the instance variable of the sender, but uses its own instance variable for calling the block

So depending on what you want to reach, one of the three is the better solution.

class Sender
  attr_accessor :send_var
  def call_a_block(var)
    rec = Receiver.new(5)
    @my_var = var
    res = rec.awesome(@my_var) do |arg1|
      arg1 + 3
    end 
    res2 = rec.awesome3(@my_var) do |arg1|
      arg1 + 3
    end
    p "Result of calling awesome with: 3 and #{@my_var} is #{res}"
    p "Result of calling awesome3 with: 3 and #{@my_var} is #{res2}"
  end
  def use_in_block(var)
    rec = Receiver.new(6)
    @my_var = var
    res = rec.awesome2 do
      4 + @my_var
    end
    p "Result of calling awesome2 with: 4 and #{@my_var} is #{res}"
  end
end

class Receiver
  attr_accessor :rec_var
  def initialize(var)
    @rec_var = var
  end
  def awesome(arg1)
    res = yield(arg1)
    res * 2
  end
  def awesome3(arg1)
    res = yield(@rec_var)
    res * 2
  end
  def awesome2
    res = yield
    res * 2
  end
end

s = Sender.new
s.call_a_block(7)
s.call_a_block(20)
s.use_in_block(7)
s.use_in_block(20)

Results are:

c:\Temp>ruby call.rb

"Result of calling awesome with: 3 and 7 is 20"
"Result of calling awesome3 with: 3 and 7 is 16"
"Result of calling awesome with: 3 and 20 is 46"
"Result of calling awesome3 with: 3 and 20 is 16"
"Result of calling awesome2 with: 4 and 7 is 22"
"Result of calling awesome2 with: 4 and 20 is 48"

2 Comments

Thanks for the explanation. Others may want to read this intro as well. My problem wasn't this, so I've asked a new question.
Also, this is a solution: [1,2,3].each {|bar| @bar=bar } taken from here.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.