是什么阻止我在 Ruby 中包含一个类?

Ste*_*fan 3 ruby class include

我试图了解一些 Ruby 的内部结构:

尝试使用include不是模块会导致TypeError:(这是设计使然的)

class C
end

class Foo
end

Foo.include(C)
#=> TypeError: wrong argument type Class (expected Module)
Run Code Online (Sandbox Code Playgroud)

我想知道类型检查“在幕后”是如何工作的。

由于类模块,我假设 Ruby 检查参数是否是以下的实际实例Module

C.is_a?(Module)        #=> true
C.instance_of?(Module) #=> false
Run Code Online (Sandbox Code Playgroud)

听起来很合理,不是吗?

但是当我定义自己的Module子类并创建该子类的实例时,它工作得很好:

class Klass < Module
end

K = Klass.new

Foo.include(K)
# no error
Run Code Online (Sandbox Code Playgroud)

K是 的一个实例Klass,就像C是 的一个实例一样Class。并且Klass是 的子类Module,就像Class

K.is_a?(Module)        #=> true
K.instance_of?(Module) #=> false

K.class #=> Klass
C.class #=> Class

Klass.superclass #=> Module
Class.superclass #=> Module
Run Code Online (Sandbox Code Playgroud)

那么该类型签入include实际上做了什么?

是否有一个隐藏属性可以让 Ruby 区分模块和类?

由于这是特定于实现的:我对 YARV/MRI 特别感兴趣。

Aet*_*rus 5

正如@Stefan 评论的那样,Module#include调用宏Check_Type(module, T_MODULE)。您可以在https://ruby-doc.org/core-2.6/Module.html#method-i-include中找到它

进一步挖掘源码,可以发现头文件ruby​​.h中有一行

#define Check_Type(v,t) rb_check_type((VALUE)(v),(t))
Run Code Online (Sandbox Code Playgroud)

soCheck_Type只是 的一个方便的别名,您可以在error.crb_check_type中找到 的定义:rb_check_type

void
rb_check_type(VALUE x, int t)  
{ 
    int xt;                    

    if (x == Qundef) {         
  rb_bug(UNDEF_LEAKED);        
    }

    xt = TYPE(x);              
    if (xt != t || (xt == T_DATA && RTYPEDDATA_P(x))) {
  unexpected_type(x, xt, t);   
    }
} 
Run Code Online (Sandbox Code Playgroud)

int t类型的唯一“ID”,也是int xt的实际类型的 ID x。您可以看到if (xt != t || ...)Check_Type检查类型等效性也是如此,而不是 is-a 关系。

长话短说

Ruby 检查包含的模块是否实际上是一个模块而不是一个类。