Skip to content

Primitives

kairo-docs-bot edited this page Jun 24, 2026 · 5 revisions

Primitives

Kairo's primitive types are built into the language and available without imports. They map directly to hardware-supported representations where possible, falling back to software emulation for extended-width types.


Integers

All integer types have a fixed, guaranteed size. The default integer type is i32 if a literal doesn't fit in i32, the compiler promotes it to the smallest signed type that can hold the value, up to i512.

Type Size Description C++ Equivalent
u8 1 byte Unsigned 8-bit integer uint8_t
u16 2 bytes Unsigned 16-bit integer uint16_t
u32 4 bytes Unsigned 32-bit integer uint32_t
u64 8 bytes Unsigned 64-bit integer uint64_t
u128 16 bytes Unsigned 128-bit integer __uint128_t
u256 32 bytes Unsigned 256-bit integer
u512 64 bytes Unsigned 512-bit integer
i8 1 byte Signed 8-bit integer int8_t
i16 2 bytes Signed 16-bit integer int16_t
i32 4 bytes Signed 32-bit integer int32_t
i64 8 bytes Signed 64-bit integer int64_t
i128 16 bytes Signed 128-bit integer __int128_t
i256 32 bytes Signed 256-bit integer
i512 64 bytes Signed 512-bit integer
usize Platform-dependent Unsigned, pointer-width integer size_t
isize Platform-dependent Signed, pointer-width integer ptrdiff_t

Integer literals default to signed. Use a type suffix to specify:

var a = 42          // i32 (default)
var b = 42u8        // u8
var c = 42i64       // i64
var d = 1_000_000   // i32 underscores are ignored, use freely as separators
var e = 0xFF        // i32 hexadecimal
var f = 0b1010_0011 // i32 binary
var g = 0o77        // i32 octal

Overflow behavior

Unsigned integer overflow wraps around (modular arithmetic). Signed integer overflow behavior depends on the build mode:

  • Debug: crashes with a diagnostic.
  • Release: wraps around silently.

This matches Rust's overflow model and catches bugs during development without paying for checks in production.

Extended-width integers (u128-u512, i128-i512)

If the target hardware supports wide registers (e.g., AVX-512), these types map directly to hardware. Otherwise, the compiler stores them as structs of smaller integers and emits SIMD-accelerated arithmetic when available, falling back to scalar multi-word operations.

Extended-width integers are always stack-allocated they are value types, not heap-allocated objects.


Floating-Point

All floating-point types follow the IEEE 754 standard. The default float type is f64 if a literal doesn't fit in f64, the compiler promotes to the smallest float type that can hold the value, up to f512.

Type Size Precision C++ Equivalent
f16 2 bytes Half (IEEE 754-2008) _Float16
f32 4 bytes Single float
f64 8 bytes Double double
f128 16 bytes Quadruple __float128
f256 32 bytes Extended (software)
f512 64 bytes Extended (software)
var x = 3.14        // f64 (default)
var y = 3.14f32     // f32
var z = 1.0e-10     // f64 scientific notation

Overflow produces inf, underflow produces 0.0. Operations that produce NaN (e.g., 0.0 / 0.0, sqrt(-1.0)) propagate NaN per IEEE 754 no crash, no trap. Check for NaN explicitly with std::is_nan() when needed.

Note

f256 and f512 are not natively supported on any current hardware and are implemented entirely in software, using SIMD instructions when available. Like extended-width integers, they are stack-allocated value types. Expect significantly lower performance compared to hardware-backed float types.


Implicit Conversions

Integer and float types can be implicitly widened i32 to i64, f32 to f64 but narrowing conversions require an explicit cast. See Casting for details.

var a: i32 = 42
var b: i64 = a      // ok: implicit widening

var c: i64 = 1000
var d: i8 = c        // compile error: narrowing requires explicit cast
var e: i8 = c as i8  // ok: explicit, may truncate

Bool

Type Size C++ Equivalent
bool 1 byte bool
var flag = true
var other = false

bool is 1 byte in memory (not 1 bit) for addressability. Only true and false are valid values no implicit conversion from integers.


Char

Type Size Description C++ Equivalent
char 4 bytes Unicode scalar value (U+0000-U+10FFFF) char32_t

A char holds a single decoded Unicode codepoint. It is always 4 bytes regardless of which codepoint it represents.

var letter = 'A'
var emoji = '😶‍🌫'
var cjk = '漢'

Note

char is the decoded representation of a single codepoint. Strings store text as UTF-8 bytes internally, not as arrays of char. See Strings below.


Byte

Type Size C++ Equivalent
byte 1 byte std::byte

byte is semantically identical to u8 in size and representation but restricted to bitwise operations and comparisons no arithmetic. It represents raw data where the value is not meant to be interpreted as a number.

var b: byte = 0xFF
var mask: byte = 0x0F
var result = b & mask   // ok: bitwise AND
// var bad = b + mask   // compile error: arithmetic not allowed on byte

Strings

Type Size Encoding C++ Equivalent
string 32 bytes UTF-8 std::string

Strings are UTF-8 encoded byte sequences. The string type uses small string optimization (SSO); short strings are stored inline without a heap allocation (exact threshold subject to change until the standard library is finalized). Longer strings are heap-allocated.

var greeting = "Hello, Kairo! 📣"   // 18 UTF-8 bytes fits in SSO
var name     = "Name"               // 7 bytes SSO

Because UTF-8 is a variable-width encoding, indexing by codepoint (s[i]) complexity depends on how the string was constructed. For string literals, the compiler pre-populates a breadcrumb cache at codegen time mapping codepoint positions to byte offsets, making indexing O(1) with zero runtime cost. For strings constructed at runtime from a raw pointer, no cache is available and indexing is O(n). Indexing by byte (s.bytes[i]) is always O(1) but returns raw u8 bytes, not characters.

var s = "Hello 📣"
s.bytes[0]    // byte: 0x48 ('H') O(1)
s[6]          // char: '📣' codepoint indexing, O(1) for literals (compiler cache), O(n) for runtime-constructed strings

for ch in s {
    // ch is char decoded codepoint, yielded sequentially
}

Important

The stdlib API for strings is still being finalized. Detailed documentation for string methods will be added in a future update.


Void

Type Size C++ Equivalent
void 0 bytes void

void indicates the absence of a value. It can be used as a normal type and as the target of an unsafe pointer (unsafe *void), using void as a normal type denotes a unit type.

fn log(msg: string) -> void {
    // ...
}

var opaque: unsafe *void = get_handle()  // raw, untyped pointer
var void_t: MyObj<void> = MyObj<void>()  // void is valid here

Pointers

Type Size Description
*T 8 bytes Safe pointer non-null, compiler-tracked
unsafe *T 8 bytes Raw pointer nullable, no safety checks

*T is a thin pointer (8 bytes). It is non-null by construction and supports pointer arithmetic when the compiler can track its provenance via AMT. See Pointers for full details.

unsafe *T is a raw C-style pointer with no compiler tracking. It can be null, and dereferencing a null

unsafe *T is undefined behavior. Use unsafe *T for C/C++ interop, custom allocators, and other low-level scenarios. See Pointers and Unsafe for full details.

var x = 42
var p: *i32 = &x                // safe pointer to x
var q: unsafe *i32 = unsafe &x  // raw pointer, no tracking

Collections

Collections are built-in generic types with literal syntax. All are heap-allocated except fixed-size arrays.

Vectors [T]

A growable, owning, contiguous array. Layout: ptr + len + cap (24 bytes).

var nums: [i32] = [1, 2, 3]
nums.push(4)
nums[0]    // 1 bounds-checked

When borrowed as const [T], a vector acts as a non-owning view with cap set to zero no growth permitted, no deallocation on drop. See Ownership for borrowing semantics.

Arrays [T; N]

A fixed-size array allocated inline (stack or struct). N must be a compile-time constant.

var rgb: [u8; 3] = [255, 128, 0]
// rgb.push(42)  // compile error: fixed size

Maps {K: V}

A hash map from keys of type K to values of type V.

var ages: {string: i32} = {"Alice": 30, "Bob": 25}
ages["Charlie"] = 35

Sets {T}

A hash set of unique elements.

var primes: {i32} = {2, 3, 5, 7, 11}

Tuples (T1, T2, ...)

A fixed-size, heterogeneous, ordered group of values. Stored contiguously with padding for alignment.

var point: (f64, f64) = (1.0, 2.0)
var record: (i32, string, bool) = (42, "Answer", true)

Function Pointers fn (T1, T2, ...) -> R

A pointer to a function with the given signature. Platform-dependent size.

fn add(a: i32, b: i32) -> i32 { return a + b }
var operator: fn (i32, i32) -> i32 = add
op(3, 4)  // 7

Important

The stdlib API for vectors, maps, and sets is still being finalized. Detailed method documentation will be added in a future update.


Platform-Dependent Sizes

usize and isize match the target platform's pointer width:

Platform usize / isize
64-bit 8 bytes
32-bit 4 bytes
16-bit 2 bytes

Summary

// Integers
var a = 42              // i32
var b = 42u8            // u8
var c = 0xFF            // i32 (hex)
var d = 0b1010          // i32 (binary)
var e = 1_000_000       // i32 (underscores as separators)

// Floats
var f = 3.14            // f64
var g = 3.14f32         // f32

// Bool, char, string
var h = true            // bool
var i = '📣'            // char (4 bytes, Unicode scalar)
var j = "Hello, Kairo!" // string (UTF-8, SSO threshold TBD)

// Byte
var k: byte = 0xFF      // raw byte, no arithmetic

// Pointers
var x = 42
var p = &x              // *i32
var q: unsafe *i32 = unsafe &x

// Collections
var nums: [i32] = [1, 2, 3]                           // vector
var rgb: [u8; 3] = [255, 128, 0]                      // array
var ages: {string: i32} = {"Alice": 30, "Bob": 25}    // map
var primes: {i32} = {2, 3, 5, 7}                      // set
var point: (f64, f64) = (1.0, 2.0)                    // tuple

// Function pointer
fn add(a: i32, b: i32) -> i32 { return a + b }
var operator: fn (i32, i32) -> i32 = add

Start here: Primitives


1. Fundamentals

2. Functions & Control Flow

3. Types

4. Modules & Metaprogramming

5. Memory & Safety

6. Interop & Concurrency


Website · Docs · Repo

Clone this wiki locally