8

Is there any difference in how class_eval & instance_eval work except def? Inside class_eval block def defines method to class itself (i.e. instance method) and inside instance_eval def defines method to the eigenclass of the class (i.e. class method). AFAIK all other features work identically in both cases (e.g. define_method, attr_accessor, class << self; end, defining constants). Is it true?

Answer is: def, undef and alias have different contexts for class_eval and instance_eval.

Alexey
  • 9,197
  • 5
  • 64
  • 76

2 Answers2

18

Long story short:

  • Object.instance_eval &block sets:
  • Object.class_eval &block sets:
    • self to Object
    • The "current class" to Object

The "current class" is used for def, undef and alias, as well as constant and class variable lookups.


Now, let's have a look at the implementation details.

Here's how module_eval and instance_eval are implemented in C:

VALUE rb_mod_module_eval(int argc, VALUE *argv, VALUE mod) {
    return specific_eval(argc, argv, mod, mod);
}

VALUE rb_obj_instance_eval(int argc, VALUE *argv, VALUE self) {
    VALUE klass;
    if (SPECIAL_CONST_P(self)) { klass = Qnil; }
    else { klass = rb_singleton_class(self); }
    return specific_eval(argc, argv, klass, self);
}

Both call specific_eval, which takes the following arguments: int argc, VALUE *argv, VALUE klass and VALUE self.

Note that:

  • module_eval passes the Module or Class instance as both klass and self
  • instance_eval passes the object's singleton class as klass

If given a block, specific_eval will call yield_under, which takes the following arguments: VALUE under, VALUE self and VALUE values.

if (rb_block_given_p()) {
    rb_check_arity(argc, 0, 0);
    return yield_under(klass, self, Qundef);
}

There are two important lines in yield_under:

  1. block.self = self;

    This sets the self of the block to the receiver.

  2. cref = vm_cref_push(th, under, NOEX_PUBLIC, blockptr);

    The cref is a linked list which specifies the "current class", which is used for def, undef and alias, as well as constant and class variable lookups.

    That line basically sets the cref to under.

    Finally:

    • When called from module_eval, under will be the Class or Module instance.

    • When called from instance_eval, under will be the singleton class of self.

Matheus Moreira
  • 17,106
  • 3
  • 68
  • 107
  • 1
    There is one thing: inside `class_eval` assigning constants and class variables does not work the way it works in class definition/reopening: it uses outer scope. – Alexey Apr 24 '12 at 21:19
  • @Alexey, you are right. I bet it has something to do with that `NODE_FL_CREF_PUSHED_BY_EVAL` constant. Many methods, like [`Module::nesting`](http://ruby-doc.org/core-1.9.3/Module.html#method-c-nesting) for example, seem to ignore a `cref` node if the flag is set. – Matheus Moreira Apr 24 '12 at 22:38
  • Shouldn't the first version be `Object.class_eval &block`? Otherwise you're not showing the different between `instance_eval` on the class and `class_eval` on the class, you're showing the difference between `instance eval` on an instance of the class and `class_eval` on the class. – Michael Hewson Jun 02 '19 at 07:52
  • @MichaelHewson, I used a class for `class_eval` because the method is only defined for `Module` instances. Classes are objects so `instance_eval` behaves similarly even if `object = Object`. The `self` will be `Object` but methods defined in the block will become class methods of `Object` because they were actually defined on `Object.singleton_class`. – Matheus Moreira Jun 03 '19 at 19:50
  • Oh sorry, I meant to say that the _second_ version, the one using `instance_eval`, should also use `Object` instead of `Object.new` so that both are being called on the `Object` class. I just think having the same method receiver would illustrate the difference between the methods better. – Michael Hewson Jun 04 '19 at 22:14
  • Oh no, that's not right either :facepalm: it's the first version that I'm talking about, the one with `instance_eval`. What I said about having the same method receiver still applies though. (Sorry for all the comments, wish I could have gotten it right the first time.) – Michael Hewson Jun 04 '19 at 22:17
  • 1
    @Michael, I see what you mean now and I agree. I will edit my answer to reflect that. – Matheus Moreira Jun 05 '19 at 10:00
0

instance_eval lets you access the instance's instance variables directly, and use self as a reference to the instance.

rcrogers
  • 2,281
  • 1
  • 17
  • 14