UniqueBox, Ref, and MutableRef in Swift 6.4


Greetings, traveler!

Swift 6.4 continues the ownership work that has been building through noncopyable types, borrowing, consuming parameters, lifetime dependencies, Span, and MutableSpan. The important part of this direction is that Swift is gaining more ways to describe ownership and access rules directly in APIs, instead of leaving them as compiler internals, documentation comments, or unsafe pointer conventions.

Two Swift Evolution proposals are central here. SE-0517 introduces UniqueBox, a standard library smart pointer that uniquely owns a value on the heap. SE-0519 introduces Ref and MutableRef, two standard library types for safe, first-class references with shared or exclusive access. UniqueBox has been accepted, while Ref and MutableRef are marked as implemented in Swift 6.4.

The gap these types are trying to close

Swift already has several ownership concepts. A function can take a borrowing parameter and receive temporary shared access to a value. A function can take an inout parameter and receive temporary exclusive access to a value owned by the caller. A function can consume a value. A noncopyable type can prevent implicit copies. These pieces already exist, but many of them are tied to function calls and local compiler reasoning.

That creates a gap. Sometimes code needs to form a safe reference to a value and pass that reference around as a value of its own. Sometimes code needs to put a large inline value on the heap while still preserving unique ownership. Before these proposals, developers commonly reached for a class box or an unsafe pointer. Both approaches work, but they carry semantics that are broader or less safe than the problem requires. SE-0519 explicitly calls out that classes introduce allocation, reference counting, and dynamic exclusivity checking overhead, while UnsafePointer requires extreme care and interacts awkwardly with Swift’s higher-level semantics.

A simple class box gives heap storage, but it also gives shared ownership:

final class Box<Value> {
    var value: Value

    init(_ value: Value) {
        self.value = value
    }
}

That can be useful, but shared ownership changes the model. Copies of the reference can exist in many places, ARC has to manage the object lifetime, and mutable state inside the box can be aliased. For some designs, shared ownership is exactly the wrong semantic contract.

UniqueBox, Ref, and MutableRef provide a more precise vocabulary. UniqueBox says that a value lives on the heap and has one owner. Ref says that code has shared read access to another value for a limited lifetime. MutableRef says that code has exclusive mutable access to another value for a limited lifetime.

UniqueBox

UniqueBox<Value> is a noncopyable standard library type that uniquely owns a value allocated on the heap. SE-0517 describes it as a smart pointer type that uniquely owns an instance of Value on the heap. The proposed declaration looks like this:

public struct UniqueBox<Value: ~Copyable>: ~Copyable {
    public init(_ initialValue: consuming Value)
}

The value inside the box is available through a value property with borrow and mutate accessors:

extension UniqueBox where Value: ~Copyable {
    public var value: Value {
        borrow
        mutate
    }

    public consuming func consume() -> Value
}

This means the box can be used for in-place reads and writes to the stored value:

var box = UniqueBox([1, 2, 3])

print(box.value)

box.value.swapAt(0, 2)

print(box.value)

The important detail is ownership. UniqueBox itself is ~Copyable, so it cannot be copied implicitly. That prevents accidental sharing of the same heap allocation through multiple owners. When the box is no longer used, it cleans up the heap allocation automatically. SE-0517 presents this as a safer alternative to manually allocating memory with UnsafeMutablePointer, initializing the memory, and remembering to deinitialize and deallocate it later.

UniqueBox and stable address

One of the central guarantees of UniqueBox is stable storage for the value it owns. SE-0517 says that a UniqueBox provides a stable address to the value allocated on the heap. The UniqueBox value itself can still be moved by the compiler, but moving the box does not move the value it allocated. The physical address of the allocated value remains stable.

That matters for large values and for APIs where address stability is part of the design. SE-0517 mentions types such as InlineArray or custom structs with many fields as cases where putting a value behind an allocation can be useful. Passing large inline values around can increase code size or create unnecessary movement of data. A heap allocation can be a better representation in some cases, while unique ownership avoids the broader semantics of a reference-counted class.

UniqueBox also provides span and mutableSpan properties for single-element span access to the stored value:

extension UniqueBox where Value: ~Copyable {
    public var span: Span<Value> {
        get
    }

    public var mutableSpan: MutableSpan<Value> {
        mutating get
    }
}

For copyable values, UniqueBox provides an explicit clone() operation:

extension UniqueBox where Value: Copyable {
    public func clone() -> UniqueBox<Value>
}

That distinction is important. UniqueBox itself remains noncopyable, because implicit copying would break unique ownership. When Value is Copyable, cloning the contents into a new unique allocation is an explicit operation.

Ref

Ref<Value> represents shared access to another value. SE-0519 describes it as a shared borrow that can read the target value without consuming or modifying it. The proposed API is:

public struct Ref<Value: ~Copyable>: Copyable & ~Escapable {
    @_lifetime(borrow target)
    public init(_ target: borrowing Value)

    public var value: Value { borrow }
}

A Ref can be copied because shared read access can have multiple readers. The target remains borrowed for the lifetime of the reference. While a dependent Ref is active, the target can be borrowed again, but mutation of the target is restricted by the borrow.

SE-0519 gives this shape of example:

var totals = [17, 38]

do {
    let apples = Ref(totals[0])

    print(apples.value)

    apples.value += 2 // error: Ref.value is read-only

    totals[1] += 1 // error: cannot mutate totals while borrowed

    print(totals[1])
    print(apples.value)
}

The exact point is that Ref turns a temporary borrow into a value with its own type. That value remains bound to the lifetime of the target through lifetime dependencies, so the compiler can prevent dangling references without relying on a runtime check.

MutableRef

MutableRef<Value> represents exclusive mutable access to another value. Its proposed API is:

public struct MutableRef<Value: ~Copyable>: ~Copyable & ~Escapable {
    @_lifetime(&target)
    public init(_ target: inout Value)

    public var value: Value {
        borrow
        mutate
    }
}

MutableRef is ~Copyable, because copying it would create multiple mutable references to the same target. That would violate Swift’s exclusivity model. While a MutableRef is active, it assumes exclusive use of the target value, and direct use of the original target is restricted until the reference lifetime ends.

SE-0519 illustrates this with an array element:

var totals = [17, 38]

do {
    var bananas = MutableRef(&totals[1])

    bananas.value += 2
    print(bananas.value)

    print(totals[1]) // error: totals is exclusively accessed by bananas

    bananas.value += 2
    print(bananas.value)
}

print(totals)

This is the same ownership shape Swift already applies to inout, but MutableRef allows that access to be represented as a first-class value. The proposal also shows a practical dictionary example where a subscripted entry is projected once and then modified repeatedly without repeated hash table lookup: (GitHub)

func updateTotal(
    in dictionary: inout [String: Int],
    for key: String,
    with values: [Int]
) {
    var entry = MutableRef(&dictionary[key, default: 0])

    for value in values {
        entry.value += value
    }
}

Why ~Escapable matters

Both Ref and MutableRef are ~Escapable. That is one of the most important parts of the design. A reference to another value must not outlive the value or access it depends on. SE-0519 states that once formed, Ref and MutableRef carry a lifetime dependency on their target. A Ref can be used only as long as the target can remain borrowed, and a MutableRef can be used only as long as the target can remain exclusively accessed.

This is what makes these types different from ordinary reference types. A class reference can be stored, copied, captured, and allowed to escape. Ref and MutableRef are scoped by the lifetime rules of the value they reference.

This also means that they can appear as fields only in types that can carry those lifetime restrictions. SE-0519 shows a Person type that stores MutableRef fields and is itself ~Copyable and ~Escapable:

struct Person: ~Copyable, ~Escapable {
    var name: MutableRef<String>
    var age: MutableRef<Int>
}

That is a major design difference from storing references in classes. The type carrying the reference also participates in the lifetime model.

Ref and MutableRef as single-value Span concepts

SE-0519 explicitly connects these types to Span and MutableSpan. A Span can be seen as shared access to multiple elements, while a MutableSpan can be seen as exclusive mutable access to multiple elements. Ref and MutableRef provide the corresponding single-value forms.

That gives a useful mental model:

Ref<Value>          // shared access to one value
MutableRef<Value>   // exclusive mutable access to one value

Span<Element>        // shared access to multiple elements
MutableSpan<Element> // exclusive mutable access to multiple elements

For iOS developers, this matters most in library and infrastructure code. Typical UIKit or SwiftUI feature code may not need to reach for these types directly, but collection implementations, parsing layers, buffer-heavy code, interop wrappers, custom storage abstractions, and performance-sensitive infrastructure can benefit from this more precise model.

Representation of Ref

Ref<Value> has an implementation detail that is easy to miss. It does not always have to be represented as a pointer. SE-0519 says that depending on the properties of Value, Ref<Value> may either be represented as a pointer to the target value in memory or as a bitwise copy of the target value’s representation. A pointer representation is used when the value is larger than four machine words, when Value is not bitwise-borrowable, or when Value is addressable-for-dependencies.

This follows Swift’s calling convention goals. Small bitwise-borrowable values, such as an Int, do not necessarily need a stable memory address for a borrow. Forcing every Ref<Int> to be a pointer could require unnecessary temporary stack storage. For types that produce lifetime-dependent views into their in-memory representation, such as InlineArray through Span, an addressable representation becomes important. SE-0519 also states that C, Objective-C, and C++ imported types are considered addressable-for-dependencies to make interaction with pointer-like relationships easier.

MutableRef has a simpler representation story. Since inout parameters are always passed by address at the machine calling convention level, MutableRef can use a pointer representation in all cases without limiting its ability to cross function call boundaries.

Sendable and BitwiseCopyable behavior

SE-0519 specifies conditional Sendable conformances for both reference types when the target value is Sendable:

extension Ref: Sendable where Value: Sendable & ~Copyable {}
extension MutableRef: Sendable where Value: Sendable & ~Copyable {}

The proposal describes this as analogous to Span and MutableSpan being Sendable when their element type is Sendable. It also specifies that Ref is always BitwiseCopyable, while MutableRef is not Copyable and therefore cannot be BitwiseCopyable. The proposal still guarantees that MutableRef will never have non-trivial move or deinit operations.

These details matter for generic code. They show that these types are designed to participate in Swift’s broader ownership, layout, and concurrency model instead of living as isolated pointer wrappers.

Nontrivial accesses and property observers

Ref and MutableRef can target more than plain stored properties. SE-0519 says they can target values produced through get/set pairs, yielded by coroutine accessors, guarded by dynamic exclusivity checks, or observed by didSet and willSet. In those cases, the access is initiated when the reference is formed and ended when the reference lifetime ends.

The proposal gives this example:

struct NoisyCounter {
    private var _value: Int

    var value: Int {
        get {
            print("counted \(_value)")
            return _value
        }
        set {
            print("updating counter to \(newValue)")
            _value = newValue
        }
    }
}

var counter = NoisyCounter(67)

do {
    var counterRef = MutableRef(&counter.value)
    counterRef.value += 1
    counterRef.value += 1
}

The access to counter.value begins when MutableRef is formed. The setter runs when the reference lifetime ends. This is a precise but important rule: the reference depends on the access, not merely on the original object or variable.

API design implications

These types make ownership part of the public shape of an API. A function that accepts UniqueBox<Value> communicates unique heap ownership. A function that accepts Ref<Value> communicates temporary shared access. A function that accepts MutableRef<Value> communicates temporary exclusive access.

That is very different from accepting a class box, an UnsafePointer, or a copied value. The choice affects copying, aliasing, lifetime, mutation, storage, and sometimes ABI details. SE-0517 also notes that UniqueBox is additive to the standard library ABI, while SE-0519 says that adding Ref and MutableRef is additive and does not affect the ABI of existing code. Generic support for Ref, however, requires new runtime type layout functionality, which can limit availability when targeting older Swift runtimes.

For application code, this means the first wave of usage will probably appear in libraries, standard-library-style abstractions, storage wrappers, performance-oriented utilities, and interop layers. For most iOS screens, the higher-level architectural choices around value state, actors, main actor isolation, and SwiftUI invalidation will still matter more. These ownership types become relevant when code needs to control storage and access with much finer precision.

How to think about the new model

A useful way to place these types is to separate ownership from access:

// Shared heap ownership
final class Box<Value> {
    var value: Value
}

// Unique heap ownership
UniqueBox<Value>

// Shared temporary access
Ref<Value>

// Exclusive temporary access
MutableRef<Value>

UniqueBox answers the storage and ownership question: where does this value live, and who owns it? Ref and MutableRef answer the access question: who may read or mutate this value during a specific lifetime?

That distinction is the real value of the feature set. Swift is adding standard vocabulary for concepts that already exist in the language model. The result is less reliance on class boxes for cases that need unique ownership, and less reliance on unsafe pointers for cases that need temporary references.

Closing thoughts

UniqueBox, Ref, and MutableRef are not everyday replacements for structs, classes, or inout. They are lower-level building blocks for code that needs stronger control over ownership, storage, and lifetime. Their importance comes from making those rules explicit and checkable.

For iOS developers, the immediate impact may be small in feature code, but the long-term direction is significant. Swift is becoming better at expressing memory and ownership relationships without leaving the safety of the type system. That matters for high-performance libraries, custom collections, C and C++ interop, buffer-oriented code, and any place where today’s options force a choice between a class with broader semantics and an unsafe pointer with fewer guarantees.