Raku 提供了一组原生类型,它们在内存中具有固定且已知的表示形式。此页面显示了哪些类型存在以及如何使用它们。请同时查看 原生数字 页面以获取有关它们的更多信息。

具有原生表示形式的类型§

Raku 中的一些简单类型具有原生表示形式,表示它们将使用编译器、操作系统和机器提供的 C 语言表示形式。以下是四种可用的原生类型

int等同于 Int(范围有限)
uint等同于 Int(范围有限),具有 unsigned 特征
num等同于 Num
str等同于 Str

但是,这些类型不一定具有 NativeCall 接口所需的尺寸(例如,Raku 的 int 可以是 8 个字节,但 C 的 int 只有 4 个字节);必须使用以下类型来代替上面列出的 intnum 类型。

通常,这些变量的行为方式与常规标量变量相同,这种行为称为 自动装箱;但是,有一些区别,因为你实际声明的是它们将如何表示,而不是它们的实际类型。第一个区别是它们的类型实际上是它们的等效类型,而不是它们的原生类型。

my int $intillo = 3;
say $intillo.^name# OUTPUT: «Int␤»

这显然意味着它们将智能匹配它们的等效(自动装箱)类型,而不是它们的原生类型

my str $strillo = "tres";
say $strillo ~~ str# OUTPUT: «False␤» 
say $strillo ~~ Str# OUTPUT: «True␤»

并且它们始终具有默认值,这与它们的非原生对应项不同

say (my Str $); # OUTPUT: «(Str)␤» 
say (my str $); # OUTPUT: «␤» 
say (my num $); # OUTPUT: «0␤»

注意:在 v6.c 中,num 的默认值将是 NaN。

这是因为原生类型不知道它们的类型,因为它们只是值,没有任何元数据。在 多重分派 中,你可以有原生候选,但你无法区分同一种原生类型的不同大小或有无符号。也就是说,你可以有 Intint 候选,但在 intuintatomicintint64 候选之间会存在歧义。

它们也不能绑定;尝试执行 my num $numillo := 3.5 将引发异常 无法绑定到原生类型变量 '$variable-name';请改用赋值

原生类型也可以是复合类型。

my int @intillos = ^10_000_000;
say [+@intillos# OUTPUT: «49999995000000␤»

在这种情况下,原生性扩展到复合类型,称为 array

my num @many-pi  = ^8 »*» π ; say @many-pi.^name;  # OUTPUT: «array[num]␤»

原生array的行为类似于IterablePositional,但它们不是List的子类;因此,它们的行为类似于Array;例如,它们可以被塑形

my str @letter-pairs[10= 'a'..'j' Z~ 'A'..'J';
say @letter-pairs.raku;
# OUTPUT: «array[str].new(:shape(10,), ["aA", "bB", "cC", "dD", "eE", "fF", "gG", "hH", "iI", "jJ"])␤» 

原生str数组只能从版本6.d开始使用。

这些原生类型也可以在类中用作属性,因此它们可以用作绑定目标,例如在BUILD等子方法中

class Foo {
    has num $.numillo;
    submethod BUILD:$!numillo = 3.5e0 ) {}
};
my $foo = Foo.new;
say $foo.raku# OUTPUT: «Foo.new(numillo => 3.5e0)␤» 

具有原生表示和大小的类型§

关于具有原生表示的类型所提到的内容也适用于这里;它们将被自动装箱为Raku类型,你将无法绑定到它们。但是,这些类型(在下面的表格中列出)具有可以在NativeCall函数中使用的特性。

int8(C中的int8_t)
int16(C中的int16_t)
int32(C中的int32_t)
int64(C中的int64_t)
byte, uint8(C中的uint8_t)
uint16(C中的uint16_t)
uint32(C中的uint32_t)
uint64(C中的uint64_t)
num32(C中的float)
num64(C中的double)

这些类型具有与平台无关的固定大小表示,因此可以安全地用于这些原生调用。如果我们愿意,没有什么可以阻止我们在任何其他环境中使用它们。与上述类型相同,在为这种类型的变量赋值时必须考虑此大小

my byte $intillo = 257;
say $intillo# OUTPUT: «1␤»

由于byte只能容纳8位,因此它将溢出并分配原始值模256的结果,这就是显示的内容。

具有声明的原生大小的类型与没有声明的原生大小的类型之间的主要区别是在其声明中使用is nativesize。例如,int8以这种方式声明

my native int8 is repr('P6int'is Int is nativesize(8{ }

表示它除了使用整数表示(P6int)之外,还将使用仅为8位的原生大小。但是,此特性不打算在你的程序中使用,因为它不属于Raku规范。

void类型§

原生void类型对应于C同名类型;作为一个有效的类型,你可以在表达式中使用它

use NativeCall;
my void $nothing;
say $nothing.raku# OUTPUT: «NativeCall::Types::void␤»

实际上,它是一个Uninstantiable类型,很少可以单独使用,事实上它在return类型中是明确禁止的。但是,它通常在表示等效于C中的void *指针的类型化指针中找到。

sub mallocint32 $size --> Pointer[void] ) is native { * };
my Pointer[void$for-malloc = malloc32 );
say $for-malloc.raku;

如示例所示,它由参数化的Pointer角色表示,使用原始指针指向的任何内容(在本例中为void)作为参数。此角色表示原生指针,可以在需要在Raku程序中表示原生指针的任何地方使用。

你还可以将Blobsnativecast为这种类型的指针,以防你需要在使用该类型的原生函数中使用它们

use NativeCall;
my Pointer[void$native = nativecast(Pointer[void], Blob.new(0x220x33));

但是,除此之外,它提供的功能非常有限,因为无法取消引用指向void的指针

use NativeCall;
my Pointer[void$native = nativecast(Pointer[void], Buf.new(0x220x33));
say $native.deref# ERROR OUTPUT: «Internal error: unhandled target type␤» 

原子类型§

在此上下文中,原子是指线程下的安全操作。Raku 提供了一个类型 atomicint,以及 一些操作,它们共同保证了这一点。请查看 数字页面上的原子操作部分 以获取更多相关信息。

Rakudo 特定的本机类型§

本部分描述的类型是 Rakudo 特有的,因此无法保证它们存在于其他实现中或在未来版本中保持不变。

long(C 中的 long)
longlong(C 中的 longlong)
ulong(C 中的 long 和 unsigned)
ulonglong(C 中的 longlong 和 unsigned)
size_t(C 中的 size_t 和 unsigned)
ssize_t(C 中的 size_t)
bool(C 中的 bool)

你可以像在原生 C 中使用它们一样使用它们

use NativeCall;
 
my $just-an-array = CArray[int32].new12345 );
 
loop ( my size_t $i = 0$i < $just-an-array.elems$i++ ) {
    say $just-an-array[$i];
}

这将按预期打印数组的五个元素。