Raku offers a set of native types with a fixed, and known, representation in memory. This page shows which ones exist and how they can be used. Please check also the page on native numerics for more information on them.
Types with native representation§
Some simple types in Raku have a native representation, indicating that they will use the C language representation provided by the compiler, operating system and machine. These are the four native types available:
int | Equivalent to Int (with limited range) |
uint | Equivalent to Int (with limited range) with the unsigned trait |
num | Equivalent to Num |
str | Equivalent to Str |
However, these types do not necessarily have the size that is required by the NativeCall interface (e.g., Raku's int
can be 8 bytes but C's int
is only 4 bytes); the types below will have to be used instead of the types int
or num
listed above.
In general, these variables will behave in the same way as regular scalar variables, in a behavior that is called auto-boxing; however, there are some differences, since what you are actually declaring is how they will be represented, not their actual type. The first one is that their type will be actually their equivalent type, not their native type.
my int $intillo = 3; say $intillo.^name; # OUTPUT: «Int»
This obviously means that they will smartmatch their equivalent (auto-boxed) type, not their native type:
my str $strillo = "tres"; say $strillo ~~ str; # OUTPUT: «False» say $strillo ~~ Str; # OUTPUT: «True»
And also that they will always have a default value, unlike their non-native counterparts:
say (my Str $); # OUTPUT: «(Str)» say (my str $); # OUTPUT: «» say (my num $); # OUTPUT: «0»
Note: In v6.c, the default value for num
would have been a NaN.
This is due to the fact that Natives don't know their types because they're just values, without any metadata. In multi-dispatch, you can have a native candidate, but you cannot differentiate different sizes or signedness of the same native type. That is, you can have an Int
and int candidates, but there would be an ambiguity between, for instance int, uint, atomicint
or int64 candidates.
They cannot be bound either; trying to do my num $numillo := 3.5
will raise the exception Cannot bind to natively typed variable '$variable-name'; use assignment instead
.
Native types can also be composite.
my int @intillos = ^10_000_000; say [+] @intillos; # OUTPUT: «49999995000000»
In this case, nativeness extends to the composite type, which is called array
my num @many-pi = ^8 »*» π ; say @many-pi.^name; # OUTPUT: «array[num]»
Native array
s behave as Iterable
and Positional
, but they are not a subclass of List
; thus, they behave similarly to Array
s; for instance, they can be shaped:
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"])»
Native arrays of str
can only be used from version 6.d.
These native types can also be used as attributes in classes, and as such they can be used as bound targets, for instance in submethods like BUILD
:
class Foo { has num $.numillo; submethod BUILD( :$!numillo = 3.5e0 ) {} }; my $foo = Foo.new; say $foo.raku; # OUTPUT: «Foo.new(numillo => 3.5e0)»
Types with native representation and size§
What has been mentioned about types with native representation also applies here; they will be auto-boxed to Raku types and you will not be able to bind to them. However, these types, which are listed in the table below, have the characteristic of being usable in NativeCall functions.
int8 | (int8_t in C) |
int16 | (int16_t in C) |
int32 | (int32_t in C) |
int64 | (int64_t in C) |
byte, uint8 | (uint8_t in C) |
uint16 | (uint16_t in C) |
uint32 | (uint32_t in C) |
uint64 | (uint64_t in C) |
num32 | (float in C) |
num64 | (double in C) |
These types have a fixed size representation which is independent of the platform, and thus can be used safely for those native calls. Nothing prevents us from using them in any other environment, if we so wish. In the same way as the types above, this size will have to be taken into account when assigning values to variables of this type:
my byte $intillo = 257; say $intillo; # OUTPUT: «1»
Since byte
is able to hold only 8 bits, it will wrap over and assign the result of the original value modulo 256, which is what is shown.
The main difference between types with declared native size and those without is the use of is nativesize in their declaration. For instance, int8
is declared in this way:
my native int8 is repr('P6int') is Int is nativesize(8) { }
Indicating that it will use, besides an integer representation (P6int
), a native size of only 8 bits. This trait, however, is not intended to be used in your programs since it is not part of the Raku specification.
The void
type§
The native void
type corresponds to the C namesake type; being a valid type, you can use it in expressions:
use NativeCall; my void $nothing; say $nothing.raku; # OUTPUT: «NativeCall::Types::void»
In practice, it is an Uninstantiable
type that can rarely be used by itself, and in fact it is explicitly forbidden in return
types. However, it is generally found in typed pointers representing the equivalent to the void *
pointer in C.
sub malloc( int32 $size --> Pointer[void] ) is native { * }; my Pointer[void] $for-malloc = malloc( 32 ); say $for-malloc.raku;
As the example shows, it's represented by a parameterized Pointer
role, using as parameter whatever the original pointer is pointing to (in this case, void
). This role represents native pointers, and can be used wherever they need to be represented in a Raku program.
You can also nativecast Blob
s to this kind of pointer in case you need to work with them in native functions that use the type
use NativeCall; my Pointer[void] $native = nativecast(Pointer[void], Blob.new(0x22, 0x33));
However, outside that, the functionality it offers is quite limited, since pointers to void cannot be dereferenced:
use NativeCall; my Pointer[void] $native = nativecast(Pointer[void], Buf.new(0x22, 0x33)); say $native.deref; # ERROR OUTPUT: «Internal error: unhandled target type»
Atomic types§
In this context, atomic refers to safe operation under threading. Raku provides a type, atomicint
, and some operations which, together, guarantee this. Please check the atomic operations section on the Numerics page for more information on this.
Rakudo specific native types§
The types described in this section are Rakudo specific, so they are not guaranteed to be in other implementations or remain the same in future versions.
long | (long in C) |
longlong | (longlong in C) |
ulong | (long and unsigned in C) |
ulonglong | (longlong and unsigned in C) |
size_t | (size_t and unsigned in C) |
ssize_t | (size_t in C) |
bool | (bool in C) |
You can use them in the same way they would be used in native C:
use NativeCall; my $just-an-array = CArray[int32].new( 1, 2, 3, 4, 5 ); loop ( my size_t $i = 0; $i < $just-an-array.elems; $i++ ) { say $just-an-array[$i]; }
Which would print the five elements of the array, as it should be expected.