简体   繁体   中英

Inconsistent behaviour with instance_eval

When passing a block to instance_eval, it is meant to be executed within the context of that instance. self , when referenced either explicitly or implicitly within that block, should refer to the instance that instance_eval has been called on. This seems to work fine in all cases, except when passing a method object that has been converted to a proc. In this case, self refers to the instance that the method is defined on, rather than the instance where the block is evaluated. Here's a code example to demonstrate what I mean:

class A
  def test(&b)
    instance_eval(&b)
  end
end

class B
  def test_a(a)
    a.test { puts self }
  end

  def test_b_helper(*args)
    puts self
  end

  def test_b(a)
    m = method(:test_b_helper).to_proc
    a.test(&m)
  end
end

a = A.new
b = B.new

b.test_a(a) #<A:0x007ff66b086c68>
b.test_b(a) #<B:0x007fa3e1886bc0>

The expected behaviour is for both tests to return the same output. In this case, self should refer to an instance of A, not B.

I have looked in the docs and done some searches, but I have not been able to find information on this peculiarity. I am hoping that there are some experienced Rubyists who can help clear up this difference in behaviour.

Just to clarify, I am using Ruby 1.9.2.

The difference is, that Blocks and Procs are closures , Method objects are not.

Excerpt from D. Flanagan, Y. Matsumoto. The Ruby Programming Language , O'Reilly 2008, p. 204:

One important difference between Method objects and Proc objects is that Method objects are not closures. Ruby's methods are intended to be completely self-contained, and they never have access to local variables outside of their own scope. The only binding retained by a Method object, therefore, is the value of self — the object on which the method is to be invoked.

When you now cast the Method object to_proc , you bind the value of self to the calling instance of B, hence you get the result you described above. Actually, self is already fixed when you create the Method object.

This gets particularly clear when you consider the following code:

class A
  def foo
    puts 'bar'
  end
end

class B; end

class C < A; end

foo = A.instance_method(:foo)
# => #<UnboundMethod: A#foo>

a = A.new
foo.bind(a).call
# bar

b = B.new
foo.bind(b).call
# TypeError: bind argument must be an instance of A

c = C.new
foo.bind(c).call
# bar

Simply put: self is always fixed on Method and UnboundMethod objects.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM