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
}
}
```