# Struct vs Class Swift: Complete Guide to Value Types and Reference Types

> Understand struct vs class swift. Explore value types (structs) and reference types (classes) comparing memory management in Swift for optimal performance.

- Repository: [Apple/swift](https://github.com/apple/swift)
- Tags: deep-dive
- Published: 2026-02-18

---

**In Swift, structs are value types that employ copy-on-write semantics and stack allocation, while classes are reference types that utilize heap allocation, support inheritance, and are managed by Automatic Reference Counting (ARC).**

When developing with the apple/swift open-source toolchain, choosing between `struct` and `class` determines your data's memory layout, thread safety, and lifecycle management. The Swift compiler distinguishes these categories at the abstract syntax tree (AST) level using `StructDecl` and `ClassDecl` nodes, enforcing distinct semantics from compilation through runtime execution.

## Value Semantics vs. Reference Semantics

The fundamental distinction in struct vs class Swift development lies in their assignment behavior.

**Structs** implement **value semantics**. When you assign a struct instance to a new variable or pass it to a function, Swift creates a complete copy of the data. According to the implementation in [`stdlib/public/core/String.swift`](https://github.com/apple/swift/blob/main/stdlib/public/core/String.swift), standard library types like `String` and `Array` use copy-on-write optimizations to maintain efficiency while preserving value semantics.

**Classes** implement **reference semantics**. Assignment operations copy only the reference (pointer) to a heap-allocated object. Multiple variables can point to the same instance, and mutations are visible through all references. As shown in [`stdlib/private/StdlibUnicodeUnittest/Collation.swift`](https://github.com/apple/swift/blob/main/stdlib/private/StdlibUnicodeUnittest/Collation.swift), reference types are essential when object identity must be preserved across different scopes.

## Memory Allocation and Performance Characteristics

Memory management represents a critical technical difference between these type categories.

**Stack Allocation for Structs**

Value types are typically allocated on the stack or inlined within containing structures. This eliminates heap allocation overhead and reference-counting operations. The Swift compiler generates efficient value-type instructions at the SIL (Swift Intermediate Language) level, as documented in [`SwiftCompilerSources/Sources/SIL/Value.swift`](https://github.com/apple/swift/blob/main/SwiftCompilerSources/Sources/SIL/Value.swift).

**Heap Allocation for Classes**

Classes require heap allocation with pointer indirection. Each class instance carries reference-counting metadata managed by ARC. The compiler injects retain and release calls for class types, whereas structs never trigger these passes. In [`SwiftCompilerSources/Sources/SIL/Instruction.swift`](https://github.com/apple/swift/blob/main/SwiftCompilerSources/Sources/SIL/Instruction.swift), you can observe reference-specific instructions like `ref_element_addr` and `ref_cast` that handle class field access and type casting.

## Inheritance and Protocol Conformance

The object-oriented capabilities of classes contrast sharply with the protocol-oriented design of structs.

**Class Inheritance**

Classes support single inheritance, allowing subclasses to override methods and properties. You can mark classes as `final` to prevent subclassing, or use `required` initializers to enforce implementation in subclasses. This inheritance hierarchy is managed through the `ClassDecl` AST node defined in [`SwiftCompilerSources/Sources/AST/Declarations.swift`](https://github.com/apple/swift/blob/main/SwiftCompilerSources/Sources/AST/Declarations.swift) at line 125.

**Struct Protocol Composition**

Structs cannot inherit from other structs or classes. Instead, they rely on protocol conformance and composition to achieve polymorphism. The `StructDecl` node (line 121 in [`Declarations.swift`](https://github.com/apple/swift/blob/main/Declarations.swift)) enforces these restrictions during semantic analysis. This design encourages protocol-oriented programming, where structs adopt protocols to gain capabilities without the overhead of inheritance hierarchies.

## Identity, Equality, and Mutability

Reference identity and mutation semantics differ significantly between the two type categories.

**Identity Operators**

Classes support the **identity operator** (`===`) to determine if two references point to the same heap instance. Structs lack intrinsic identity; you cannot use `===` with value types. Equality for structs is determined by comparing all stored properties.

**Mutating Methods**

Structs require the `mutating` keyword on methods that modify `self`, explicitly indicating that the method changes the value. This requirement stems from value semantics where the entire instance may be replaced. Classes do not use `mutating`; instance methods can modify properties through any reference unless the instance itself is declared with `let`.

## Practical Code Examples

### Value Semantics with Copy-on-Write

The following example demonstrates how structs create independent copies:

```swift
struct Point {
    var x: Double
    var y: Double
    
    mutating func move(dx: Double, dy: Double) {
        x += dx
        y += dy
    }
}

var a = Point(x: 0, y: 0)
var b = a                // Creates a full copy of the value
b.move(dx: 5, dy: 5)     // Mutates only b

print(a)   // Point(x: 0.0, y: 0.0) - unchanged
print(b)   // Point(x: 5.0, y: 5.0)

```

### Reference Semantics and Identity

This example illustrates class behavior with shared references:

```swift
class Counter {
    var value = 0
    
    func increment() {
        value += 1
    }
}

let c1 = Counter()
let c2 = c1               // Both reference the same heap instance
c2.increment()

print(c1.value)           // 1 - visible through both references
print(c1 === c2)          // true - identity check confirms same object

```

### Inheritance vs. Protocol Composition

Demonstrating the architectural differences:

```swift
// Class inheritance hierarchy
class Vehicle {
    var speed = 0
}

class Car: Vehicle {
    var wheels = 4
}

// Struct with protocol conformance
protocol Drivable {
    var speed: Int { get set }
}

struct Bike: Drivable {
    var speed = 0
}

```

### Managing Reference Cycles

Using weak references to prevent retain cycles in class hierarchies:

```swift
class Owner {
    var pet: Pet?
}

class Pet {
    weak var owner: Owner?   // weak prevents retain cycle
}

var person = Owner()
var dog = Pet()
person.pet = dog
dog.owner = person
// No retain cycle - either can be deallocated independently

```

## Summary

- **Value vs. Reference**: Structs use value semantics (copy-on-write) while classes use reference semantics (shared heap instances).
- **Memory Management**: Structs avoid ARC overhead and typically use stack allocation; classes require heap allocation and ARC management.
- **Inheritance**: Classes support single inheritance and identity checking (`===`); structs use protocol composition and lack inheritance.
- **Compiler Implementation**: The Swift compiler distinguishes these via `StructDecl` (line 121) and `ClassDecl` (line 125) in [`SwiftCompilerSources/Sources/AST/Declarations.swift`](https://github.com/apple/swift/blob/main/SwiftCompilerSources/Sources/AST/Declarations.swift), generating different SIL instructions for value types versus reference types.
- **Mutability**: Structs require the `mutating` keyword for self-modification; classes modify properties through any reference.

## Frequently Asked Questions

### When should I use a struct versus a class in Swift?

Use **structs** for simple data containers, mathematical types, and models that represent values without identity, such as coordinates or configuration options. Use **classes** when you need reference identity, inheritance hierarchies, or shared mutable state across multiple parts of your application, such as view controllers or network managers.

### Why do structs provide better thread safety than classes?

Structs provide **natural thread safety** through value semantics. When you pass a struct to another thread, Swift creates a copy of the data, ensuring that mutations in one thread cannot affect the instance in another thread. Classes share a single heap instance across threads, requiring explicit synchronization mechanisms like locks or actors to prevent data races.

### What is the performance impact of using classes instead of structs?

Classes incur **heap allocation overhead** and **ARC management costs** that structs avoid. Each class instance requires memory allocation from the heap and reference counting operations when references are created or destroyed. Structs typically use stack allocation or inline storage, eliminating indirection and retain/release cycles. However, for large value types, copy-on-write optimizations in the standard library help mitigate copying costs.

### Can structs inherit from other types in Swift?

No, **structs cannot inherit** from other structs or classes. Swift structs support protocol conformance and composition instead of inheritance. If you need inheritance behavior, you must use a class, which supports single inheritance from another class. This design encourages protocol-oriented programming and prevents the complexity of multiple inheritance hierarchies in value types.