# Swift Structs Explained: Properties, Methods, and Value Types

## Pro Summary

Structs have copy semantics at the language level, but copies are cheap only because standard-library collections implement copy-on-write. Your own struct gets CoW only if you write it with `isKnownUniquelyReferenced`. A struct that stores class references copies the references, so its value semantics go only as deep as its stored properties. Non-public structs whose stored properties are all `Sendable` are implicitly `Sendable`, but a `public` struct isn't implicitly `Sendable` to other modules (unless it's `@frozen`), and its synthesized memberwise initializer is never `public`. For unique-ownership resources, Swift 5.9's `~Copyable` structs add `consuming` and `borrowing` semantics.

```
struct Money: Hashable, Sendable {
    var amountMinor: Int          // store cents, not Double
    let currency: String
    var note = ""                 // defaulted parameter in the memberwise init
}

extension Money {
    init(dollars: Int) { self.init(amountMinor: dollars * 100, currency: "USD") }   // memberwise init survives
}
```

Requires: Any Swift version. Defaulted `var` parameters in the memberwise initializer: Swift 5.1+ (SE-0242). `~Copyable` structs: Swift 5.9+ (SE-0390). `Sendable` violations are errors in the Swift 6 language mode.

Gotchas:

- Declaring any `init` in the struct body removes the synthesized memberwise initializer. Put custom initializers in an extension to keep both.
- `let` properties with an initial value are left out of the memberwise initializer (passing them is an 'extra argument' error), while `var` properties with defaults become optional arguments.
- Copying a struct that holds a class instance shares the instance: `copy.ref.n = 99` changes the original too, and even a `let` struct allows `w.ref.n = 5`.
- A `private(set) var` keeps the memberwise initializer internal because only the setter is private. A single `private` stored property makes the whole memberwise initializer private, so other files can't call it.
- In Swift 6, `Task.detached { counter.increment() }` on a local `var` struct is a data-race error ('sending value of non-Sendable type ... risks causing data races'). Capture a copy with `[counter]`, or move the mutable state into an actor.

Primary sources:

- [The Swift Programming Language: Structures and Classes](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/classesandstructures/)
- [The Swift Programming Language: Initialization (memberwise initializers)](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/initialization/)
- [SE-0242: Synthesize default values for the memberwise initializer](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0242-default-values-memberwise.md)
- [SE-0302: Sendable and @Sendable closures](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0302-concurrent-value-and-concurrent-closures.md)

Source: https://devdocs10.com/articles/swift/swift-structs  
By Murat Bo · Updated 2026-10-17 · DevDocs10 (https://devdocs10.com)

Swift structs are one of the most important tools for creating your own types. They let you group related data and behavior into a single reusable unit, and they are used throughout Swift because they are safe, fast, and predictable. In this article, you will learn what structs are, how to define them, how their value semantics work, and how to avoid the most common mistakes developers make when using them.

> **Quick answer:** A Swift struct is a custom value type that can store properties, define methods, and represent related data as one unit. When you copy a struct or assign it to another variable, Swift copies the value instead of sharing the same instance.

> **Difficulty:** Beginner
>
> **Helpful to know first:** You will understand this better if you already know basic Swift syntax, variables and constants, functions, and simple types like `String`, `Int`, and `Bool`.

## 1. What Is a Struct?

A struct in Swift is a custom type that groups related values and behavior together. You can think of it as a blueprint for creating values that have named properties and methods.

- A struct is declared with the `struct` keyword.
- Structs are **value types**, which means copies are independent.
- They can contain stored properties, computed properties, methods, and initializers.
- They are commonly used for models like users, products, points, sizes, and settings.
- They are often compared with classes because both define custom types, but structs copy values while classes share references.

For example, if you want to represent a user with a name and age, a struct gives you a clean way to keep that data together instead of storing separate unrelated variables.

## 2. Why Structs Matter

Structs matter because they match the way many real program values behave. A screen size, a date range, a shopping cart item, or a point on a graph is usually best treated as a value, not as a shared object.

Swift encourages using structs by default because they help reduce accidental shared state. When values are copied instead of shared, code becomes easier to reason about and bugs caused by unexpected mutation become less common.

Use structs when:

- You want to model data as a value.
- Each copy should be independent from the original.
- You do not need inheritance.
- You want simpler, safer data modeling.

Structs are not always the right choice. If you need shared identity, inheritance, or reference semantics, a class may be more appropriate. That comparison appears again later in this article because it is one of the most common Swift design decisions.

## 3. Basic Syntax

The basic syntax of a struct starts with the `struct` keyword, followed by a name and a body in braces.

### Declaring a simple struct

This example defines a `Book` struct with two stored properties.

```swift
struct Book {
    var title: String
    var pageCount: Int
}
```

This does not create a book yet. It only defines the shape of the type.

### Creating a value from a struct

Because both properties are stored properties and no custom initializer is defined, Swift provides a memberwise initializer automatically.

```swift
let swiftBook = Book(title: "Learning Swift", pageCount: 320)
```

The memberwise initializer lets you pass values for each stored property by name.

### Accessing properties

You use dot syntax to read properties.

```swift
print(swiftBook.title)
print(swiftBook.pageCount)
```

Output (tested with Swift 6.4):

```text
Learning Swift
320
```

This prints the title and page count stored inside the struct value.

### Adding methods

Structs can also define methods, which are functions that belong to the type.

```swift
struct Rectangle {
    var width: Double
    var height: Double

    func area() -> Double {
        return width * height
    }
}
```

The `area()` method uses the struct's own properties to calculate a result.

## 4. Step-by-Step Examples

The best way to understand structs is to see them in realistic examples.

### Example 1: Storing related user data

Here, a struct groups a user's name and age into one value.

```swift
struct User {
    var name: String
    var age: Int
}

let user = User(name: "Maya", age: 28)
print(user.name)
print(user.age)
```

Output (tested with Swift 6.4):

```text
Maya
28
```

This is cleaner than passing separate name and age values all over your code.

### Example 2: Using a computed property

A computed property calculates a value instead of storing it directly.

```swift
struct Temperature {
    var celsius: Double

    var fahrenheit: Double {
        (celsius * 9 / 5) + 32
    }
}

let today = Temperature(celsius: 21)
print(today.fahrenheit)
```

Output (tested with Swift 6.4):

```text
69.8
```

The struct stores one value and derives another when needed.

### Example 3: Mutating a struct

Because structs are value types, methods that change properties must be marked with `mutating`.

```swift
struct Counter {
    var count = 0

    mutating func increment() {
        count += 1
    }
}

var counter = Counter()
counter.increment()
print(counter.count)
```

Output (tested with Swift 6.4):

```text
1
```

The `mutating` keyword tells Swift that the method changes the struct's stored data.

### Example 4: Demonstrating value semantics

This example shows one of the most important differences between structs and classes.

```swift
struct Point {
    var x: Int
    var y: Int
}

var start = Point(x: 0, y: 0)
var end = start

end.x = 10

print(start.x)
print(end.x)
```

Output (tested with Swift 6.4):

```text
0
10
```

Changing `end` does not change `start` because assigning a struct creates a copy.

## 5. Practical Use Cases

Structs are useful in many real projects, especially when modeling values.

- Representing app data such as users, products, orders, and addresses.
- Creating small geometry types such as points, sizes, and rectangles.
- Modeling settings or configuration values that should be copied safely.
- Returning grouped results from functions instead of multiple separate values.
- Defining domain-specific value types such as money amounts, coordinates, or scores.
- Creating simple data containers with helper methods for formatting or validation.

> Swift's own standard library uses structs heavily. Types like `String`, `Int`, `Array`, and `Dictionary` are all value-oriented types, which matches Swift's general design style.

## 6. Common Mistakes

### Mistake 1: Forgetting `mutating` on a method that changes properties

Methods inside a struct are non-mutating by default. If a method changes a stored property, Swift requires the `mutating` keyword.

**Problem:** This method changes `count` without being marked as mutating, so Swift reports an error similar to `Cannot assign to property: 'self' is immutable`.

```swift
struct Counter {
    var count = 0

    func increment() {
        count += 1
    }
}
```

**Fix:** Mark the method as `mutating` so Swift knows the struct value will change.

```swift
struct Counter {
    var count = 0

    mutating func increment() {
        count += 1
    }
}
```

The corrected version works because `mutating` allows the method to modify the struct's stored properties.

### Mistake 2: Trying to change a struct stored in a constant

Even if the properties inside a struct are declared with `var`, the whole value becomes immutable when the struct instance is stored in a `let` constant.

**Problem:** This code tries to change a property on a constant struct value, so Swift reports an error such as `Cannot assign to property: 'item' is a 'let' constant`.

```swift
struct Task {
    var title: String
}

let item = Task(title: "Write article")
item.title = "Review article"
```

**Fix:** Use `var` for the struct value if you need to modify any of its properties.

```swift
struct Task {
    var title: String
}

var item = Task(title: "Write article")
item.title = "Review article"
```

The corrected version works because the struct value itself is mutable.

### Mistake 3: Expecting two struct variables to share changes

Beginners often assign one struct value to another and expect both names to refer to the same underlying object.

**Problem:** This code assumes that changing `copy` will also change `original`, but structs are copied on assignment.

```swift
struct Score {
    var points: Int
}

var original = Score(points: 10)
var copy = original
copy.points = 20
print(original.points)
```

Output (tested with Swift 6.4):

```text
10
```

**Fix:** Treat structs as independent values after assignment. If you need shared identity and shared mutation, consider whether a class is the better model.

```swift
struct Score {
    var points: Int
}

var original = Score(points: 10)
var copy = original
copy.points = 20

print(original.points) // 10
print(copy.points) // 20
```

Output (tested with Swift 6.4):

```text
10
20
```

The corrected version works because it reflects the real behavior of value semantics.

## 7. Best Practices

### Use structs by default for simple data models

In Swift, starting with a struct is often the best choice. It gives you predictable value semantics and avoids unnecessary shared state.

```swift
struct Product {
    var name: String
    var price: Double
}
```

This is a strong default for data that represents a value rather than a shared object.

### Keep related behavior inside the struct

If a piece of logic naturally belongs to the data, place it on the struct as a method or computed property. This keeps code organized and easier to read.

```swift
struct Circle {
    var radius: Double

    func area() -> Double {
        3.14159 * radius * radius
    }
}
```

This approach keeps the calculation close to the data it depends on.

### Prefer constants when values should not change

If a struct instance is not supposed to change, store it in a `let` constant. This makes your intent clear and prevents accidental mutation.

```swift
struct Configuration {
    var apiURL: String
}

let config = Configuration(apiURL: "https://api.example.com")
```

Using a constant here communicates that the configuration value should remain stable.

## 8. Limitations and Edge Cases

- Structs do not support inheritance. If your design depends on subclassing, a class is required.
- Mutating methods cannot be called on struct values stored in a `let` constant.
- When a struct contains reference types as properties, the struct itself is copied, but the referenced objects may still be shared.
- Swift provides a memberwise initializer for many structs, but adding your own custom initializer can affect how that automatic initializer is available.
- Large structs are still value types, but Swift uses optimizations such as copy-on-write in some standard library types to keep performance practical.
- If your code relies on object identity, structs are the wrong tool because value types do not have reference identity in the same sense as classes.

## 9. Swift Structs vs Classes

Structs and classes are both custom types in Swift, but they are designed for different modeling needs.

| Feature | Struct | Class |
| --- | --- | --- |
| Semantics | Value type | Reference type |
| Copy behavior | Copied on assignment | Shared reference on assignment |
| Inheritance | No | Yes |
| Identity | No shared object identity | Has reference identity |
| Common use | Data values and models | Shared mutable state or inheritance-based designs |

Choose a struct when each value should stand on its own. Choose a class when multiple parts of your program need to refer to the same shared object.

```swift
struct Position {
    var x: Int
}

var a = Position(x: 1)
var b = a
b.x = 5
print(a.x) // 1
```

Output (tested with Swift 6.4):

```text
1
```

With a struct, `b` becomes a separate copy.

> A common Swift guideline is: start with a struct, and switch to a class only when you need reference semantics or inheritance.

## 10. Practical Mini Project

This mini project creates a simple bank account struct. It stores data, uses methods, and demonstrates mutation clearly.

```swift
struct BankAccount {
    var owner: String
    private(set) var balance: Double

    mutating func deposit(amount: Double) {
        if amount > 0 {
            balance += amount
        }
    }

    mutating func withdraw(amount: Double) {
        if amount <= balance {
            balance -= amount
        }
    }

    func summary() -> String {
        "\(owner) has $\(balance)"
    }
}

var account = BankAccount(owner: "Chris", balance: 100)
account.deposit(amount: 50)
account.withdraw(amount: 30)
print(account.summary())
```

Output (tested with Swift 6.4):

```text
Chris has $120.0
```

This example shows several useful ideas together: stored properties, access control, mutating methods, and a regular method that returns a formatted description. It is a good example of a value type because each account value can be copied safely and changed independently.

## 11. Key Points

- A Swift struct is a custom type that groups related data and behavior.
- Structs are value types, so assignment and passing create independent copies.
- They can contain stored properties, computed properties, methods, and initializers.
- Methods that change stored properties must be marked with `mutating`.
- A struct stored in a `let` constant cannot have its properties changed.
- Structs are usually the best starting point for data models in Swift.
- Classes are better only when you need shared identity, inheritance, or reference semantics.

## 12. Practice Exercise

Try building your own struct to reinforce the core ideas.

- Create a struct named `Movie`.
- Add stored properties for `title`, `year`, and `rating`.
- Add a method named `description()` that returns a formatted string.
- Add a mutating method named `updateRating(to:)` that changes the rating.
- Create a movie, print its description, update the rating, and print the description again.

**Expected output:** Two printed lines showing the same movie before and after the rating change.

**Hint:** If a method changes a property inside a struct, you need the `mutating` keyword.

```swift
struct Movie {
    var title: String
    var year: Int
    var rating: Double

    func description() -> String {
        "\(title) (\(year)) - Rating: \(rating)"
    }

    mutating func updateRating(to newRating: Double) {
        rating = newRating
    }
}

var movie = Movie(title: "Skyline", year: 2024, rating: 7.2)
print(movie.description())
movie.updateRating(to: 8.1)
print(movie.description())
```

Output (tested with Swift 6.4):

```text
Skyline (2024) - Rating: 7.2
Skyline (2024) - Rating: 8.1
```

## 13. Final Summary

Swift structs are a core part of the language and one of the best tools for modeling data clearly. They let you define custom value types with properties, methods, and computed values, while giving you the safety and predictability of value semantics.

In this article, you learned how to declare structs, create instances, use methods, write mutating behavior, and understand how copying works. You also saw common mistakes such as forgetting `mutating` or trying to change a struct stored in a constant.

A good next step is to learn more about Swift classes and initializers so you can choose the right custom type for each situation. Once you understand both structs and classes well, designing Swift models becomes much easier.

## Where AI Coding Assistants Get This Wrong

- Often suggested: Declares `public struct Point { public var x: Int; public var y: Int }` in a package and has client code call `Point(x:y:)`.
  Instead: Write an explicit `public init(x: Int, y: Int)`, because the synthesized memberwise initializer is at most `internal`. (Clients fail to compile with "'Point' initializer is inaccessible due to 'internal' protection level".)
- Often suggested: Claims that a struct wrapping a class, or any large struct, automatically gets copy-on-write, or that copying a struct deep-copies everything inside it.
  Instead: Only standard-library types such as `Array`, `Dictionary`, and `String` implement CoW. For custom reference storage, check `isKnownUniquelyReferenced(&storage)` before mutating and clone the storage when it's shared. (Without that check, copies share one class instance and mutate each other.)
- Often suggested: Expects a `public struct` whose stored properties are all `Sendable` to be `Sendable` in other modules automatically, or patches the error with `@unchecked Sendable`.
  Instead: Declare `: Sendable` explicitly on public types, and the compiler will check the stored properties for you. (Implicit `Sendable` inference covers only non-public types and `@frozen` public ones. Other modules get "type 'X' does not conform to the 'Sendable' protocol".)
