| name | swift-memory |
| description | Swift 6.2 InlineArray and Span types for zero-overhead memory access, fixed-size collections, and safe pointer alternatives. Use when optimizing performance-critical code paths. |
First step: Tell the user: "swift-memory skill loaded."
InlineArray and Span
Guidance for Swift 6.2's low-level memory types: InlineArray for fixed-size inline storage without heap allocation, and Span for safe, zero-cost access to contiguous memory. These replace common uses of UnsafeBufferPointer and hand-tuned tuple storage with compiler-checked alternatives.
When This Skill Activates
Use this skill when the user:
- Asks about InlineArray, fixed-size arrays, or stack-allocated collections
- Mentions Span, MutableSpan, RawSpan, or UTF8Span
- Wants to eliminate heap allocations in hot paths
- Is replacing UnsafeBufferPointer or UnsafePointer with safe alternatives
- Asks about value generics or
let count: Int generic parameters
- Needs zero-copy access to collection storage
- Mentions inline storage, contiguous memory, or memory layout
- Is doing binary parsing, signal processing, or embedded Swift work
- Wants to avoid copy-on-write overhead for small fixed collections
- Asks about non-escapable types or lifetime dependencies in Swift
Decision Tree
What memory optimization do you need?
│
├─ A fixed-size collection that never grows/shrinks
│ │
│ ├─ Size known at compile time, stored on stack
│ │ └─ InlineArray<N, Element>
│ │ ├─ No heap allocation
│ │ ├─ No reference counting
│ │ └─ No copy-on-write (eager copies)
│ │
│ └─ Size may vary at runtime
│ └─ Array<Element> (standard library)
│
├─ Safe read access to contiguous memory
│ │
│ ├─ Read-only access to typed elements
│ │ └─ Span<Element>
│ │
│ ├─ Mutable access to typed elements
│ │ └─ MutableSpan<Element>
│ │
│ ├─ Read-only access to raw bytes
│ │ └─ RawSpan
│ │
│ ├─ Mutable access to raw bytes
│ │ └─ MutableRawSpan
│ │
│ ├─ Unicode text processing
│ │ └─ UTF8Span
│ │
│ └─ Initializing a new collection's storage
│ └─ OutputSpan
│
├─ Unsafe pointer access (legacy or interop)
│ └─ UnsafeBufferPointer / UnsafeMutableBufferPointer
│ └─ Prefer Span instead for new code
│
└─ Standard dynamic collection
└─ Array<Element>
├─ Heap-allocated, copy-on-write
└─ Grows/shrinks dynamically
API Availability
| API | Minimum Version | Notes |
|---|
InlineArray<let count: Int, Element> | Swift 6.2 | Uses value generics; @frozen struct |
Span<Element> | Swift 6.2 | Non-escapable, lifetime-dependent |
MutableSpan<Element> | Swift 6.2 | Mutable variant of Span |
RawSpan | Swift 6.2 | Untyped byte-level access |
MutableRawSpan | Swift 6.2 | Mutable untyped byte access |
UTF8Span | Swift 6.2 | Unicode-aware text processing |
OutputSpan | Swift 6.2 | For initializing collection storage |
.span property on Array | Swift 6.2 | Returns Span<Element> |
.span property on Data | Swift 6.2 | Returns Span<UInt8> |
Top 5 Mistakes
| # | Mistake | Fix |
|---|
| 1 | Trying to append/remove elements on InlineArray | InlineArray is fixed-size; use Array if you need dynamic sizing |
| 2 | Returning a Span from a function | Span is non-escapable and cannot outlive its source; restructure to process data within the same scope |
| 3 | Capturing a Span in a closure | Span cannot be captured; pass the span as a parameter or use Array for escaped contexts |
| 4 | Using InlineArray for large or frequently copied collections | InlineArray copies eagerly (no COW); use Array for large data that is shared or copied often |
| 5 | Accessing a Span after mutating the source container | Mutation invalidates the span; re-acquire the span after any modification |
InlineArray
Declaration
InlineArray uses Swift's value generics to encode the count in the type:
@frozen struct InlineArray<let count: Int, Element> where Element: ~Copyable
Initialization
let a: InlineArray<4, Int> = [1, 2, 4, 8]
let b: InlineArray<_, Int> = [1, 2, 4, 8]
let c: InlineArray<4, _> = [1, 2, 4, 8]
let d: InlineArray = [1, 2, 4, 8]
Memory Layout
Elements are stored contiguously with no overhead. Size equals count * MemoryLayout<Element>.stride:
MemoryLayout<InlineArray<0, UInt16>>.size
MemoryLayout<InlineArray<0, UInt16>>.stride
MemoryLayout<InlineArray<3, UInt16>>.size
MemoryLayout<InlineArray<3, UInt16>>.stride
MemoryLayout<InlineArray<3, UInt16>>.alignment
Basic Usage
var array: InlineArray<3, Int> = [1, 2, 3]
array[0] = 4
for i in array.indices {
print(array[i])
}
var copy = array
copy[0] = 99
InlineArray vs Array
| Characteristic | InlineArray | Array |
|---|
| Storage | Inline (stack or enclosing type) | Heap-allocated buffer |
| Size | Fixed at compile time | Dynamic |
| Copy semantics | Eager (full copy) | Copy-on-write |
| Reference counting | None | Yes (buffer reference) |
| Exclusivity checks | None | Yes |
| Append/remove | Not supported | Supported |
Span Family
Span (Read-Only)
Provides safe, direct access to contiguous typed memory. Non-escapable -- cannot outlive the source.
let array = [1, 2, 3, 4]
let span = array.span
let first = span[0]
let count = span.count
for element in span {
print(element)
}
MutableSpan
Mutable access to contiguous storage:
var array = [1, 2, 3, 4]
array.withMutableSpan { span in
for i in span.indices {
span[i] *= 2
}
}
RawSpan
Untyped byte-level access for binary data:
let data = Data([0xFF, 0x00, 0xAB, 0xCD])
let rawSpan = data.span
UTF8Span
Specialized for safe and efficient Unicode processing of UTF-8 encoded text.
Safety Constraints
Non-Escapable: Cannot Return from Functions
Span has a lifetime dependency on the container it was derived from. It cannot be returned:
func getSpan() -> Span<UInt8> {
let array: [UInt8] = Array(repeating: 0, count: 128)
return array.span
}
func processData(_ array: [UInt8]) {
let span = array.span
let sum = span.reduce(0, +)
}
Non-Escapable: Cannot Capture in Closures
func makeClosure() -> () -> Int {
let array: [UInt8] = Array(repeating: 0, count: 128)
let span = array.span
return { span.count }
}
func countElements(_ array: [UInt8]) -> Int {
let span = array.span
return span.count
}
Invalidation on Mutation
Modifying the source container invalidates any existing span:
var array = [1, 2, 3]
let span = array.span
array.append(4)
var array = [1, 2, 3]
array.append(4)
let span = array.span
let x = span[0]
Performance Considerations
When InlineArray Wins
- Small, fixed-size data (e.g., color components, matrix rows, coordinate tuples)
- Hot loops where heap allocation / ARC overhead is measurable
- Embedded Swift or contexts with no allocator
- Struct fields that should be stored inline rather than behind a pointer
When Array Wins
- Collection size is unknown or variable
- Collection is large and frequently shared (COW avoids full copies)
- Collection is passed across module boundaries where dynamic sizing is needed
When Span Wins Over UnsafePointer
- All new code that needs contiguous memory access -- Span provides the same performance with compile-time safety
- Binary parsing where you read structured data from a byte buffer
- Algorithm implementations that operate on slices of memory
When UnsafePointer Is Still Needed
- C interop requiring raw pointer parameters
- Legacy APIs that predate Span adoption
- Interfacing with system calls that take pointer arguments
When to Use / When NOT to Use
Use InlineArray When
- You know the exact element count at compile time
- You need zero heap allocation (stack storage)
- The collection is small (< ~64 elements as a guideline)
- You modify in place but rarely copy the whole collection
- You are writing performance-critical or embedded code
Do NOT Use InlineArray When
- The collection size varies at runtime
- The collection is large and frequently copied (no COW means expensive copies)
- You need to append, insert, or remove elements
- You benefit from sharing storage across variables
Use Span When
- You need fast, safe, read-only access to contiguous memory
- You want to replace
UnsafeBufferPointer with a safe alternative
- You are processing data in place without needing to escape the reference
- You are implementing high-performance algorithms on collection storage
Do NOT Use Span When
- You need to store the reference for later use (Span is non-escapable)
- You need to pass memory access across async boundaries
- You need mutable access (use
MutableSpan instead)
- C interop requires actual
UnsafePointer arguments
Review Checklist
InlineArray
Span
General Performance
References