ajhahn.de
← Flash
Markdown 133 lines
# Chapter 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`.

```flash
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`:

```flash
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.

```flash
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:

```flash
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:

```flash
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:

```flash
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 *type*`fn(P, …) R`, usually behind a pointer (`*fn(u8) u8`) — can name an explicit calling convention between its parameter list and return type:

```flash
// 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.