是什么阻止我在Ruby中包含类?
我试图理解一些Ruby的内部结构: 试图是什么阻止我在Ruby中包含类?,ruby,class,include,Ruby,Class,Include,我试图理解一些Ruby的内部结构: 试图包含类而不是模块,会导致类型错误:(这是出于设计) 我想知道“引擎盖下”的型式检验是如何工作的 由于类是模块,我假设Ruby检查参数是否是模块的实际实例: C.is_a?(Module) #=> true C.instance_of?(Module) #=> false 听起来很合理,不是吗 但是,当我定义自己的模块子类并创建该子类的实例时,它工作得很好: class Klass < Module end K = Kla
包含类而不是模块,会导致类型错误
:(这是出于设计)
我想知道“引擎盖下”的型式检验是如何工作的
由于类是模块,我假设Ruby检查参数是否是模块的实际实例:
C.is_a?(Module) #=> true
C.instance_of?(Module) #=> false
听起来很合理,不是吗
但是,当我定义自己的模块
子类并创建该子类的实例时,它工作得很好:
class Klass < Module
end
K = Klass.new
Foo.include(K)
# no error
那么,签入包含的类型实际上是做什么的呢
是否有一个隐藏属性允许Ruby区分模块和类
因为这是特定于实现的:我对YARV/MRI特别感兴趣。正如@Stefan评论的那样,Module\include
调用宏检查类型(Module,T\u Module)
。你可以在一本书中找到这个
进一步挖掘源代码,您可以发现在头文件ruby.h中有一行
#define Check_Type(v,t) rb_check_type((VALUE)(v),(t))
因此,Check\u Type
只是rb\u Check\u Type
的一个方便的别名,您可以在error.c中找到rb\u Check\u 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);
}
}
int t
是一个类型的唯一“ID”,而int xt
是x
的实际类型的ID。您可以看到if(xt!=t | | |…)
,因此Check_Type
正在检查类型等价性,而不是is-a关系
TL;博士
Ruby检查所包含的模块是否实际上是一个模块而不是一个类。我在这里回答我自己的问题
是否有一个隐藏属性允许Ruby区分模块和类
确实有。在内部,所有Ruby对象都以一个名为RBasic
的结构开始:
struct RBasic{
价值标志;
常数值klass;
};
在RBasic
中,我们有标志
,这些标志包含类型信息:
module M ; end
class C ; end
Marshal.dump(M) #=> "\x04\bm\x06M"
Marshal.dump(C) #=> "\x04\bc\x06C"
Marshal.dump(4) #=> "\x04\bi\t"
# ^
# m = module, c = class, i = integer
enum ruby\u值\u类型{
RUBY\u T\u NONE=0x00,
RUBY\u T\u对象=0x01,
RUBY\u T\u CLASS=0x02,
RUBY\u T\u模块=0x03,
RUBY\u T\u FLOAT=0x04,
RUBY\u T\u字符串=0x05,
// ...
RUBY\u T\u MASK=0x1f
};
这就是Ruby在进行类型检查时最终检查的内容:
#定义RB_内置类型(x)(int)(((struct RBasic*)(x))->标志和RUBY_T_掩码)
RB\u内置类型
也被Marshal
用来转储类型信息:
module M ; end
class C ; end
Marshal.dump(M) #=> "\x04\bm\x06M"
Marshal.dump(C) #=> "\x04\bc\x06C"
Marshal.dump(4) #=> "\x04\bi\t"
# ^
# m = module, c = class, i = integer
在Ruby中,我们可以通过以下方式检查内部类型:
现在,类型从0x02
(类)更改为0x03
(模块):
然后我们开始了:一个课程包含在另一个课程中
注意:我在这里摆弄Ruby的内部结构。不要在生产中使用这种技巧。您已收到警告。可能它会直接检查这是否是类的实例。
K.is_a?(Class)#=>false
,C.is_a?(Class)#=>true
@MarekLipka在我看到的C源代码中Check_Type(module,T_module)
,即显式检查T_模块,但没有像“not T T_Class”这样的反比
module M ; end
class C ; end
Marshal.dump(M) #=> "\x04\bm\x06M"
Marshal.dump(C) #=> "\x04\bc\x06C"
Marshal.dump(4) #=> "\x04\bi\t"
# ^
# m = module, c = class, i = integer
require 'fiddle'
def type(obj)
struct = Fiddle::Pointer.new(obj.object_id << 1)
flags = struct[0]
flags & 0x1f
end
module M ; end
class C ; end
type(M) #=> 3 (RUBY_T_MODULE = 0x03)
type(C) #=> 2 (RUBY_T_CLASS = 0x02)
class C
def hello
'hello from class'
end
end
class Foo
end
Foo.include(C)
#=> TypeError: wrong argument type Class (expected Module)
require 'fiddle'
struct = Fiddle::Pointer.new(C.object_id << 1)
struct[0] = (struct[0] & ~0x1f) | 0x03
Foo.include(C)
# NoMethodError: undefined method `append_features' for C:Class
C.define_singleton_method(:append_features, Module.instance_method(:append_features))
Foo.include(C)
# no error!
Foo.ancestors
#=> [Foo, C, Object, BasicObject, Object, Kernel, BasicObject]
Foo.new.hello
#=> "hello from class"