Zith is statically typed with a rich set of built-in types and user-defined type constructors.
| Category | Types |
|---|---|
| Unsigned integers | u8, u16, u32, u64, u128 |
| Signed integers | i8, i16, i32, i64, i128 |
| Floats | f32, f64 |
| Other primitives | bool, char, void |
| Compiler-internal | unknown (unresolved), invalid (dead/uninitialised) |
| Special | noreturn, null, File, Folder |
| Opaque | opaque (tagged void*), unique opaque (owned), raw opaque (untagged, C interop) |
| Syntax | Meaning |
|---|---|
[]T |
Slice — fat pointer (pointer + length). String literals are []char. |
[N]T |
Fixed-size array, stack-allocated. |
[_]T |
Deduced-size array — compiler infers N from the initialiser. |
NRA tracks the origin of every string node (literal, allocator, stack):
// []char implicitly casts to string — zero cost (literal origin)
let s: string = "hello";
// Concatenation changes origin to allocator — triggers allocation
let greeting = "hello" + " " + "world";
ℹ Note: string is a built-in library type. []char is the primitive representation.
A closed set of named constants. All values must be known at compile time. Three styles:
enum Direction { North, South, East, West }
enum Status: i32 { Ok = 0, Err = 1, Pending = 2 }
enum Color: rgb {
red = { r: 255, g: 50, b: 0, a: 255 },
green = { 0, 255, 0, 255 },
}
union Shape {
Circle = { radius: f32 },
Rect = { w: f32, h: f32 },
Point,
}
enum Constants: union {
pi = 3.14f,
vector = |x: -1, y: 0, z: -1, w: 1|,
nothing = 0,
}
fn area(s: Shape): f32 {
when (s) {
Circle = s.radius * s.radius * Constants.pi,
Rect = s.w * s.h,
Point = Constants.nothing,
}
}
Individual fields for unrelated fields; [] groups for semantically related fields of the same type:
struct Sample { name: string, age: i32 }
struct Point { [x, y, z]: f32 }
struct Transform {
[x, y, z]: f32, // position
[rx, ry, rz]: f32, // rotation
}
struct Pair<T, U> { first: T, second: U }
implement Pair<T, U> {
fn swap(self: lend) {
let tmp = self.first;
self.first = self.second;
self.second = tmp;
}
}
A plain-old-data (POD) struct. Copy by default (alongside primitives). C-compatible layout.
component Vec2 {
[x, y]: f32,
fn length(self): f32 { sqrt(self.x * self.x + self.y * self.y) }
fn dot(self, other: Self): f32 { self.x * other.x + self.y * other.y }
}
Components cannot implement traits, cannot contain self-referential fields, and must be pure data with only inline pure-transformation functions.
By default a union is runtime tagged. Variants separated by commas.
union Value { i32, f64, bool }
let x: union = when (flag) { 0 = 42, 1 = 3.14, 2 = true };
raw union is a C-union, only valid inside raw contexts.
| |)A pack unifies variadics, named tuples, and explicit closure captures.
// Bare pack — named tuple
let pos = |x: 1.0, y: 2.0, z: 0.0|;
@println(pos.x); // named access
@println(pos.0); // positional access
// Closure = pack + () + body
let addBase = |view base| (n: i32): i32 { base + n };
// Explicit capture
let f = |base| (n: i32): i32 { base + n };
is) & Flow TypingThe is operator narrows a union within a branch. After the block, the type widens back to the full union.
fn handle(v: Val): void {
if (v is i32) {
@println("int: {v}"); // v is i32 here
} else (v is f64) {
@println("float: {v}");
} else {
@println("str: {v}"); // compiler knows v is []char here
}
// v is Val again (full union)
}
// Explicit
fn serialize<T: Serializable + Printable, U: Clone>(val: T, ctx: U): string { ... }
// Implicit — constraints inferred from usage
fn add(a, b) { a + b }
when — Pattern Matchingwhen (count) {
0 = @println("none"),
1 = @println("one"),
2..10 = @println("few"),
_ = @println("many"),
}
// As expression
let label = when (score) { 90..100 = "A", 70..90 = "B", _ = "C" };
let n: i32 = 42;
let f = n as f64;
See also: Spec: Type System
Next: Mutability & Bindings