0
0
Swiftprogramming~15 mins

Structs are value types (copy on assign) in Swift - Deep Dive

Choose your learning style9 modes available
Overview - Structs are value types (copy on assign)
What is it?
In Swift, structs are a way to group related data together. Unlike classes, structs are value types, which means when you assign a struct to a new variable or pass it to a function, Swift makes a copy of the data. This copying behavior ensures each variable has its own independent copy of the data.
Why it matters
This copying behavior helps prevent unexpected changes when multiple parts of a program use the same data. Without value types like structs, changing data in one place could accidentally change it somewhere else, causing bugs that are hard to find. Value types make programs safer and easier to understand.
Where it fits
Before learning about structs as value types, you should understand basic Swift variables and data types. After this, you can learn about classes, which are reference types, to compare how data is shared differently.
Mental Model
Core Idea
Assigning or passing a struct creates a full copy, so each variable owns its own independent data.
Think of it like...
It's like making a photocopy of a document: when you give someone a copy, they have their own paper to write on without changing your original.
Original Struct
┌───────────────┐
│ name: "Alex"  │
│ age: 30       │
└───────────────┘

Copy on Assign
┌───────────────┐     ┌───────────────┐
│ name: "Alex"  │ --> │ name: "Alex"  │
│ age: 30       │     │ age: 30       │
└───────────────┘     └───────────────┘

Changes to one copy do not affect the other.
Build-Up - 6 Steps
1
FoundationWhat is a Struct in Swift
🤔
Concept: Introduce the basic idea of a struct as a container for related data.
A struct in Swift groups multiple pieces of data into one unit. For example, a struct Person can hold a name and age together: struct Person { var name: String var age: Int } You can create a Person like this: var person1 = Person(name: "Alex", age: 30)
Result
You have a single variable person1 that holds a name and age together.
Understanding structs as simple containers is the first step to seeing how they behave differently from other types.
2
FoundationValue Types vs Reference Types
🤔
Concept: Explain the difference between value types and reference types in simple terms.
In Swift, some types are value types, meaning they copy their data when assigned or passed around. Structs are value types. Classes are reference types, meaning they share the same data when assigned. Example: var a = 5 var b = a // b gets a copy of 5 b = 10 // a is still 5 This is how structs behave too.
Result
Assigning a value type creates a new independent copy.
Knowing the difference between value and reference types helps you predict how data changes affect your program.
3
IntermediateCopy on Assign Behavior of Structs
🤔Before reading on: do you think changing a copy of a struct changes the original? Commit to your answer.
Concept: Show that assigning a struct to a new variable copies all its data, so changes to one copy don't affect the other.
Example: struct Point { var x: Int var y: Int } var p1 = Point(x: 1, y: 2) var p2 = p1 // copy created p2.x = 10 print(p1.x) // prints 1 print(p2.x) // prints 10 Changing p2 does not change p1 because p2 is a copy.
Result
The original struct remains unchanged after modifying the copy.
Understanding copy on assign prevents bugs where you expect one variable to change but it doesn't, or vice versa.
4
IntermediatePassing Structs to Functions Copies Data
🤔Before reading on: do you think passing a struct to a function changes the original struct? Commit to your answer.
Concept: Passing a struct to a function creates a copy inside the function, so changes inside do not affect the original unless marked inout.
Example: func movePoint(_ point: Point) { var point = point point.x += 5 print("Inside function: \(point.x)") } var p = Point(x: 0, y: 0) movePoint(p) print("Outside function: \(p.x)") Output: Inside function: 5 Outside function: 0 The original p is unchanged.
Result
Function works with a copy, so original data stays safe.
Knowing this helps you decide when to use inout parameters or classes if you want shared mutable data.
5
AdvancedCopy-on-Write Optimization in Swift Structs
🤔Before reading on: do you think Swift always copies the entire struct immediately on assign? Commit to your answer.
Concept: Swift uses a technique called copy-on-write to delay copying large structs until they are actually modified, improving performance.
For large structs like arrays or dictionaries, Swift doesn't copy data immediately on assignment. Instead, it shares the data until one copy changes it. Then Swift makes a real copy. This saves memory and speeds up your program. Example: var arr1 = [1, 2, 3] var arr2 = arr1 // no immediate copy arr2.append(4) // now arr2 copies and changes arr1 remains [1, 2, 3], arr2 is [1, 2, 3, 4]
Result
Copy happens only when needed, balancing safety and speed.
Understanding copy-on-write explains why value types can be efficient even when copied often.
6
ExpertImplications of Value Semantics in Concurrency
🤔Before reading on: do you think value types like structs are safer to use in concurrent code than reference types? Commit to your answer.
Concept: Value types' copying behavior makes them naturally safer in concurrent programming because each thread works with its own copy, avoiding data races.
When multiple threads access shared data, reference types can cause conflicts if not synchronized. Structs avoid this because each thread gets its own copy. Example: DispatchQueue.concurrentPerform(iterations: 10) { i in var localCopy = sharedStruct localCopy.value += i print(localCopy.value) } Each thread modifies its own copy safely.
Result
Using structs reduces bugs in concurrent code by design.
Knowing this helps you choose value types for safer, simpler concurrent programs.
Under the Hood
Swift structs are stored directly in memory where the variable lives. When you assign a struct to another variable, Swift copies the entire data to a new memory location. For large structs, Swift uses copy-on-write, which means it shares the memory until one copy changes the data, triggering a real copy. This is managed by Swift's runtime and compiler optimizations to balance safety and performance.
Why designed this way?
Swift was designed to encourage safe and predictable code. Value types prevent unintended side effects by isolating data copies. Copy-on-write was added to avoid performance problems with large data. This design contrasts with reference types, which share data but require careful management to avoid bugs.
┌───────────────┐       copy       ┌───────────────┐
│ Struct in var │ ───────────────> │ Struct in var │
│ memory A      │                 │ memory B      │
└───────────────┘                 └───────────────┘

Copy-on-Write:

┌───────────────┐
│ Shared Buffer │
└───────────────┘
     ↑       ↑
     │       │
  var A    var B

When var B changes:

┌───────────────┐       copy       ┌───────────────┐
│ Shared Buffer │ ───────────────> │ New Buffer    │
└───────────────┘                 └───────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Does changing a copy of a struct always change the original? Commit to yes or no.
Common Belief:Changing a copy of a struct also changes the original because they share the same data.
Tap to reveal reality
Reality:Each struct variable has its own independent copy, so changing one does not affect the other.
Why it matters:Believing this leads to confusion and bugs when you expect data to update everywhere but it doesn't.
Quick: Do structs always copy their data immediately on assignment? Commit to yes or no.
Common Belief:Structs always copy all their data immediately when assigned to a new variable.
Tap to reveal reality
Reality:Swift uses copy-on-write optimization to delay copying large structs until they are modified.
Why it matters:Thinking copying is always immediate can make you avoid structs unnecessarily, missing out on performance benefits.
Quick: Is it safe to share structs between threads without synchronization? Commit to yes or no.
Common Belief:Structs can cause data races just like classes if shared between threads.
Tap to reveal reality
Reality:Because structs are copied on assign, each thread gets its own copy, making them safer for concurrent use.
Why it matters:Misunderstanding this can lead to overcomplicated synchronization or bugs in concurrent code.
Quick: Does passing a struct to a function always modify the original struct? Commit to yes or no.
Common Belief:Passing a struct to a function allows the function to modify the original struct directly.
Tap to reveal reality
Reality:Passing a struct copies it; the function works on the copy unless the parameter is marked inout.
Why it matters:This misconception causes unexpected bugs when changes inside functions don't affect the original data.
Expert Zone
1
Swift's copy-on-write is transparent but only applies to certain standard library types like Array and Dictionary, not all structs.
2
Mutating methods on structs trigger copying if the struct is shared, which can cause subtle performance costs if not understood.
3
Using structs with reference type properties mixes value and reference semantics, which can confuse the expected copy behavior.
When NOT to use
Avoid using structs when you need shared mutable state or identity, such as managing complex object graphs or UI elements. Use classes instead, which are reference types and allow shared references.
Production Patterns
In production, structs are used for data models, configuration, and small value objects to ensure safety and clarity. Copy-on-write collections like Array are heavily used for performance. Developers carefully design APIs to use inout parameters or classes when shared mutation is needed.
Connections
Immutable Data Structures
Builds-on
Understanding structs as value types helps grasp immutable data patterns where data never changes but new copies are created, improving safety.
Functional Programming
Shares principles
Value types align with functional programming ideas of avoiding side effects by working with copies, making code easier to reason about.
Copy-on-Write in Operating Systems
Same pattern
The copy-on-write optimization in Swift structs is similar to how operating systems optimize memory by sharing pages until modified, showing a cross-domain efficiency technique.
Common Pitfalls
#1Expecting changes to a copied struct to affect the original.
Wrong approach:var p1 = Point(x: 1, y: 2) var p2 = p1 p2.x = 10 print(p1.x) // expecting 10 but gets 1
Correct approach:var p1 = Point(x: 1, y: 2) var p2 = p1 p2.x = 10 print(p2.x) // 10 print(p1.x) // 1
Root cause:Misunderstanding that structs are copied on assignment, so changes to one copy don't affect others.
#2Modifying a struct inside a function expecting the original to change without inout.
Wrong approach:func updatePoint(_ point: Point) { var point = point point.x = 5 } var p = Point(x: 0, y: 0) updatePoint(p) print(p.x) // expecting 5 but gets 0
Correct approach:func updatePoint(_ point: inout Point) { point.x = 5 } var p = Point(x: 0, y: 0) updatePoint(&p) print(p.x) // 5
Root cause:Not using inout means the function works on a copy, so original remains unchanged.
#3Assuming copy-on-write applies to all structs automatically.
Wrong approach:struct LargeData { var data: [Int] } // expecting copy-on-write for LargeData var d1 = LargeData(data: [1,2,3]) var d2 = d1 // modifying d2.data triggers copy-on-write only on the array, not LargeData itself
Correct approach:Use copy-on-write aware types like Array inside structs, but understand the struct itself is copied fully on assign.
Root cause:Confusing copy-on-write optimization of standard library types with all structs.
Key Takeaways
Structs in Swift are value types, meaning assigning or passing them copies their data.
Copying ensures each variable has its own independent data, preventing unintended side effects.
Swift uses copy-on-write optimization to delay copying large data until modification, improving performance.
Passing structs to functions works on copies unless marked inout, so original data stays safe.
Value semantics make structs safer and simpler to use in concurrent programming compared to reference types.