ajhahn.de
← all chapters

Flash tutorial · 5 / 12

5. Data Types & Pointers

Flash is a statically typed programming language. This means that variables must have a defined type, which is checked at compile-time.


1. Primitive Types

Flash contains a standard set of systems-level primitive types:

Type Size / Category Description Example
u8 8-bit Unsigned integer (byte) var value u8 = 255
u32 32-bit Unsigned integer var value u32 = 42000
i32 32-bit Signed integer var value i32 = -15
usize Pointer size Unsigned size type (size of memory address) var len usize = 512
bool Boolean Store either true or false var ok bool = true
void Special Empty or no value Used for functions returning nothing
noreturn Special Indication that a function never returns Used for main exit calls

2. Pointer Types

Pointers store memory addresses and allow direct access to memory. In Flash, pointers have distinct safety and usage categories:

Single-item Pointers (*T / *mut T)

Points to exactly one value of type T in memory.

  • *T: Pointer to a constant (immutable) value of type T. This is the default.
  • *mut T: Pointer to a mutable value of type T.
var value i32 = 42
// Create a mutable pointer to value
const ptr *mut i32 = &value

// Dereference the pointer to read/write the value
ptr.* = 100

Many-item Pointers ([*]T / [*]mut T)

Points to an array of unknown size in memory. No bounds checking is performed.

  • [*]T: Points to a read-only sequence of items.
  • [*]mut T: Points to a mutable sequence of items.

Sentinel-terminated Pointers ([*:s]T)

A many-item pointer terminated by a specific sentinel value s.

  • Most commonly used for C-style strings: [*:0]u8 (null-terminated string).

Alignment-Qualified Pointers (*align(N) T)

A pointer or slice type can carry an explicit alignment, written directly after the prefix and before any mut / volatile:

const page []align(16) u8 = getBuffer()
var reg *align(4) mut volatile u32 = mapRegister()

The alignment is part of the pointer type's identity — *align(4) u32 and *u32 are distinct types. Sentinel forms compose too: [*:0]align(2) mut u8.

Built-in Pointer Aliases

To make systems programming and C interoperability cleaner, Flash provides two built-in pointer aliases:

  • cstr — translates to [*:0]const u8 (a null-terminated constant byte pointer, representing a C string).
  • argv — translates to [*]const ?[*:0]const u8 (an array of null-terminated C strings, representing command line arguments).

3. Pointer Dereferencing (p.*)

To access or modify the value at a pointer's memory address, use the .* suffix notation.

var x u32 = 10
var px *mut u32 = &x

// Access and modify via dereference
px.* = 20 // Now x is 20

[!NOTE] When accessing fields on pointers to structs, dereferencing is done automatically by the compiler. You can write ptr.field directly instead of ptr.*.field.


4. Type Aliases

A type is a compile-time value, so naming one is an ordinary const — and composite types are expressions, so no wrapper is needed:

const Byte = u8            // a primitive alias
const Bytes = []u8         // a slice alias
const MaybeLen = ?usize    // an optional alias
const Handler = *fn(u8) u8 // a function-pointer alias

Aliases work at file scope and inside functions, and a composite type can sit directly in a generic argument (List([]u8)). One boundary: the infix error union E!T cannot be the value of an alias directly — name the error set and compose it at the use site instead.


5. Tuple Types & Multiple Return Values

A parenthesized list of two or more types is a tuple type, spellable wherever a type is — a return, a parameter, an alias, or nested as an element:

const Pair = (u8, bool)

// A function returning two values: the quotient and the remainder
fn divmod(a u32, b u32) (u32, u32) {
    return a / b, a % b
}

A return heading a statement takes a comma-separated value list — Go's multi-return idiom. The tuple value literal stays .{ … }, and elements read by index:

t := .{ 42, true }
n := t[0]

The cleanest way to consume a multi-value return is a destructuring bind (q, r := divmod(7, 2) — see Chapter 4). A one-element (T) stays plain grouping, and a parenthesized value list like (1, 2) is rejected with a hint steering to .{ … }.


6. Function Types & Calling Conventions (callconv)

A function typefn(P, …) R, usually behind a pointer (*fn(u8) u8) — can name an explicit calling convention between its parameter list and return type:

// An optional C-ABI function pointer: the v-table field shape
const Handler = ?*fn(u32, *mut [512]u8) callconv(.c) i32

This is the form a driver table's entries take when they cross an ABI boundary. The convention sits exactly where a function declaration puts it, and it is part of the type's identity at compile time — fn() callconv(.c) and fn() are distinct types.