Swift 6.2 separates storage ownership from safe borrowed access
Swift 6.2 adds InlineArray, Span, and opt-in strict memory safety. Its concurrency controls also make execution context an explicit choice without changing every project's defaults automatically.
A function processing bytes should not always need to know which container owns them. Requiring an array can impose a representation choice on the caller. Accepting an unsafe buffer avoids that restriction but transfers lifetime and bounds obligations to programmers. Swift 6.2 adds a safe borrowed view of contiguous storage alongside a new fixed-size owning representation, giving those two jobs separate types.
Swift 6.2 was released on September 15. Its systems-programming changes include Span, InlineArray, and opt-in strict memory safety checking. The release also supplies concurrency controls that make it easier to retain the caller's execution context. These mechanisms share a useful direction, expressing relationships that otherwise live in conventions about memory or scheduling.
I find Swift's value semantics appealing, while recognizing that references stored within a value can still alias. The new collection types improve the precision of those choices. An owning inline value and a temporary borrowed view answer different questions, which makes their separate treatment useful for low-level library interfaces.
Owning a fixed number of elements
SE-0453 defines InlineArray as fixed-size storage whose count is part of its type. Its elements reside inline with the value. The collection does not introduce a separate implicit heap allocation merely to hold those elements.
Inline is relative to the enclosing allocation. A local value can reside on the stack, while an inline array stored as a property of a class resides with that class on the heap. The type's guarantee concerns how it stores its elements, so describing every inline array as a stack allocation would be inaccurate.
The release includes the concise type spelling accepted in SE-0483. A small coordinate value can therefore declare its fixed count directly:
let point: [3 of Double] = [1.0, 2.0, 3.0]
let zeros: InlineArray<3, Double> = .init(repeating: 0)
These declarations use equivalent type spellings. The count is fixed, so this is not a collection that can append indefinitely. Every element is initialized. SE-0453 distinguishes this model from a possible fixed-capacity collection whose logical length could vary within reserved storage.
Copy behavior follows the element capabilities. The type can hold noncopyable elements and is conditionally copyable when its elements are copyable. This allows the representation to express ownership constraints that a universally copyable wrapper could not. A fixed count and an inline layout do not imply that copying every possible element is valid.
Borrowing storage without choosing its owner
SE-0447 motivates Span using a decoder that might receive bytes from several container types. A generic sequence interface can complicate performance across library boundaries. An unsafe buffer offers contiguous access but does not provide the safety guarantees expected from ordinary Swift code.
Span describes a borrowed region of initialized contiguous memory. It does not copy the underlying elements or own a replacement container. Its lifetime is constrained by the storage it borrows, and bounds checking protects indexed access. These properties let a function process contiguous values without requiring that every caller first create the same owning collection.
The lifetime constraint is independent of the count. A pointer and a correct length can still become invalid after its storage is destroyed. Conversely, valid storage does not make an arbitrary index safe. Span's design addresses both relationships instead of treating one as a substitute for the other.
Its interface also differs from an ordinary Collection. SE-0447 explains that established collection protocols assume copyability and escapability properties that a borrowed view cannot universally provide. A span must not escape the lifetime of its source. That restriction is essential to avoiding a use-after-free without retaining or copying the underlying storage.
The distinction is useful when deciding what a library should return. A caller that needs to retain values independently still needs ownership. A temporary view can avoid a copy only while its lifetime relationship remains valid. Borrowing is an interface contract, not a general mechanism for making arbitrary storage permanent.
Strict checking identifies unsafe implementation boundaries
Swift already allows unsafe constructs for low-level code and interoperability. Swift 6.2 adds a mode that identifies their use so a project can replace them or acknowledge the obligations explicitly. SE-0458 defines the compiler option and the source annotations supporting that mode.
The flag enables warnings in the StrictMemorySafety diagnostic group. It applies to uses in the module being compiled. This invocation enables the checking for a source file named Example.swift:
swiftc -strict-memory-safety Example.swift
This is opt-in checking. It does not automatically forbid every unsafe construct in every Swift project. The diagnostic grouping also lets a project apply warning policy specifically to this category. Acknowledging a use in source records an obligation whose correctness still depends on the implementation.
The implemented design uses unsafe expressions, alongside @unsafe and @safe declaration annotations. That is more specific than the broad implementation sketches in the earlier vision document. An unsafe type in a signature can make the declaration implicitly unsafe, while a safe wrapper can establish the necessary invariants internally.
This distinction permits safe abstractions over low-level storage. A collection implementation must eventually manipulate memory, but its public subscript can enforce bounds and lifetime rules for callers. Strict checking makes the places that require additional reasoning visible without requiring every consumer to adopt a pointer-level interface.
I value this as an auditing aid for code with strong safety requirements. The marker does not prove the implementation correct. It identifies where the compiler's ordinary guarantees need support from a human-maintained contract, which is more useful than assuming that a safe public name describes every internal operation.
Async execution remains a separate relationship
Swift 6.2 also delivers the design for nonisolated async functions to run in their caller's execution context. The release describes this as an upcoming feature that projects can migrate to. Installing the compiler does not mean that every existing module silently adopts that changed default.
Under the earlier behavior, a nonisolated async call leaves an actor for the global executor. Passing a non-Sendable value owned by that actor can then create a data-race concern, because the actor is free to run other tasks while the callee uses the value elsewhere. Retaining the caller's context avoids introducing that boundary through an ordinary helper call.
The explicit @concurrent annotation remains available when a function should leave the actor. The release's image example uses it for fetching and decoding work that should keep the main actor free. A function's ability to suspend and its need to run outside the actor are distinct properties.
There is also an option to infer main-actor isolation by default. This supports executable targets and UI code that benefit from serialized state without repeated annotations. It is a configuration choice with a particular intended use, not a statement that every Swift library now belongs on the main actor.
These settings make the concurrency contract more expressive. A helper can stay with its caller's state, while an explicitly concurrent operation establishes a transfer boundary. The memory model still requires the values crossing that boundary to satisfy the applicable safety rules.
Tooling supports the source-level changes
The release includes migration tooling to identify code that will fail or change behavior when an upcoming feature is enabled. Fix-its can preserve existing intent during adoption. That is especially relevant for a concurrency setting whose effects can change without a method's basic spelling changing.
Debugging also gains more reliable async stepping and visible task context. Human-readable task names can appear in debugging and profiling tools. These capabilities help relate a suspended operation back to the task responsible for it, which becomes harder when reasoning only from a thread backtrace.
For build performance, SwiftPM adds support for prebuilt swift-syntax dependencies. The release identifies building swift-syntax from source as an expensive step for clean builds using macro APIs. Avoiding that step addresses a concrete part of the build process, although the announcement supplies no universal elapsed-time improvement for every project.
Build times remain my least favorite part of shipping small Swift applications, so that change is directly relevant to my assessment of the release. It targets work required before the macro project can build, rather than depending on source changes inside the application itself.
The practical choice between the new storage types follows the data's ownership requirements. InlineArray owns a statically sized sequence, while Span borrows existing contiguous memory under lifetime constraints. Strict checking identifies the unsafe operations still needed beneath or beside those abstractions. Concurrency settings separately determine which actor context protects the work that uses them.