ajhahn.de
← Flash
Markdown 146 lines
# Chapter 6: Structs, Enums & Unions

Flash provides powerful composite type systems: structs for group data, enums for labeled choices, and tagged unions for dynamic variants.

---

## 1. Structs

Structs group multiple related variables (fields) into a single type.

### Defining a Struct
In Flash, struct fields are defined as `name type` (without a colon or comma). A struct can also carry associated functions (methods) and constants.

```flash
const Point = struct {
    x i32,
    y i32,

    // An associated constant
    pub const origin = Point{ .x = 0, .y = 0 }

    // A method taking a pointer receiver
    pub fn distanceSquared(self *Point, other *Point) i32 {
        dx := self.x - other.x
        dy := self.y - other.y
        return (dx * dx) + (dy * dy)
    }
}
```

### Instantiating Structs
Flash supports both **anonymous struct literals** (`.{}`) and **typed struct literals**:

```flash
// Anonymous literal (type inferred from context)
var p1 Point = .{ .x = 10, .y = 20 }

// Typed literal
p2 := Point{ .x = -5, .y = 12 }

// Empty struct literal (all fields undefined / default)
var p3 Point = .{}
```

### Layout Modifiers (`packed struct` / `extern struct`)
By default the compiler is free to order and pad struct fields. Two modifiers pin the layout, prefixing the keyword:

* **`packed struct`** — the fields pack bit-exactly, in order, with no padding. The shape for an on-disk format whose byte offsets must never move.
* **`extern struct`** — the fields are laid out by the C ABI, for a type that crosses an ABI boundary and must look identical on both sides.

```flash
const DiskHeader = packed struct {
    magic u32,
    flags u8,
    count u8,
}
```

Field defaults and associated declarations work unchanged under either layout. Two boundaries: the backing-integer form `packed struct(uN)` is not part of the grammar — the field widths define the layout — and the modifiers apply to structs only (`packed union` and `extern enum` are rejected).

---

## 2. Enums

Enums define a set of named, integer-backed constant values.

### Basic Enums & Backing Types
You can define an enum with an optional backing integer type using `enum(T)`:

```flash
const Status = enum(u8) {
    ok,
    pending,
    failed,
}
```

### Explicit Discriminants
You can assign explicit integer values to enum variants using the `=` operator:

```flash
const ErrorCode = enum(u32) {
    success = 0,
    permission_denied = 1,
    out_of_memory = 12,
    io_error = 1 << 3, // bit-shift discriminants are supported!
}
```

### Inferred Enum Literals
When the expected type of an enum is known by the compiler, you can write `.variant` instead of `EnumName.variant`.

```flash
var state Status = .pending

fn check(s Status) bool {
    return s == .ok
}
```

---

## 3. Tagged Unions

Tagged unions store one value at a time out of several possible types. They carry an active "tag" that identifies which field is currently stored.

### Defining a Tagged Union
In Flash, you define a tagged union using the `union(enum)` syntax. Fields can carry payload types, or be empty (void).

```flash
const PayloadValue = union(enum) {
    integer i32,
    float f32,
    text cstr,
    none, // void variant
}
```

### Instantiating Unions
Create a union instance by specifying the active variant and its value:

```flash
var val = PayloadValue{ .integer = 42 }
var empty = PayloadValue{ .none = .{} } // Void payload
```

---

## 4. Methods & Constants in Enums and Unions

The associated declarations a struct body carries — `fn` methods, `const` constants, and `use` imports — are accepted inside `enum` and `union` bodies too, under the same layout rule: **variants first, then the declarations**.

```flash
const Status = enum(u8) {
    ok,
    pending,
    failed,

    pub const initial = Status.pending

    pub fn isDone(self Status) bool {
        return self == .ok || self == .failed
    }
}
```