0
0
Swiftprogramming~15 mins

In-out parameters for mutation in Swift - Deep Dive

Choose your learning style9 modes available
Overview - In-out parameters for mutation
What is it?
In-out parameters in Swift allow a function to modify a variable passed into it directly. Instead of passing a copy, the function works with the original variable, so changes inside the function affect the variable outside. This is useful when you want a function to update or change the value of a variable you provide. You mark these parameters with the keyword 'inout' to show that the function can change them.
Why it matters
Without in-out parameters, functions can only work with copies of variables, so any changes inside the function don't affect the original data. This means you would need to return new values and reassign them outside, which can be less clear and more error-prone. In-out parameters let you write cleaner code when you want to update variables directly, making your programs easier to read and maintain.
Where it fits
Before learning in-out parameters, you should understand how functions and variables work in Swift, especially how values are passed to functions. After mastering in-out parameters, you can explore more advanced topics like closures capturing variables, reference types, and memory management.
Mental Model
Core Idea
In-out parameters let a function borrow a variable to change it directly, so the original variable outside the function updates with the changes made inside.
Think of it like...
It's like lending a friend your notebook to write notes directly on your pages instead of copying the notes later. Whatever your friend writes is immediately on your notebook.
Function call with in-out parameter:

Caller: [variable] ──▶ Function (inout parameter)

Inside function: works on the original variable

After function: [variable] has updated value

─────────────────────────────
| func modify(value: inout Int) |
| {                            |
|    value += 10               |
| }                            |
─────────────────────────────
Build-Up - 7 Steps
1
FoundationUnderstanding value parameters
🤔
Concept: Functions receive copies of variables by default, so changes inside don't affect the original.
In Swift, when you pass a variable to a function, the function gets its own copy. For example: func addOne(number: Int) { var number = number number += 1 print(number) } var myNumber = 5 addOne(number: myNumber) print(myNumber)
Result
The function prints 6, but outside the function, myNumber remains 5.
Understanding that parameters are copies by default explains why changes inside functions don't affect variables outside.
2
FoundationIntroducing in-out parameters
🤔
Concept: In-out parameters allow functions to modify the original variable, not just a copy.
To let a function change a variable, mark the parameter with 'inout' and pass the variable with '&': func addOne(number: inout Int) { number += 1 } var myNumber = 5 addOne(number: &myNumber) print(myNumber)
Result
The output is 6, showing the original variable was changed by the function.
Knowing how to use 'inout' and '&' lets you write functions that update variables directly, making code clearer.
3
IntermediateRules for using in-out parameters
🤔Before reading on: Do you think you can pass constants or expressions as in-out parameters? Commit to your answer.
Concept: In-out parameters require variables that can be changed; constants or expressions can't be passed.
You must pass a variable with '&' to an in-out parameter. You cannot pass a constant or a computed expression: let constant = 10 // addOne(number: &constant) // Error: Cannot pass constant // addOne(number: &(5 + 3)) // Error: Cannot pass expression var variable = 10 addOne(number: &variable) // Works fine
Result
Only variables can be passed as in-out parameters; constants or expressions cause errors.
Understanding this prevents common errors and clarifies that in-out parameters need a real storage location to modify.
4
IntermediateIn-out parameters with multiple variables
🤔Before reading on: Can a function have more than one in-out parameter? Predict yes or no.
Concept: Functions can have multiple in-out parameters to modify several variables at once.
Example: func swapInts(a: inout Int, b: inout Int) { let temp = a a = b b = temp } var x = 3 var y = 7 swapInts(a: &x, b: &y) print("x: \(x), y: \(y)")
Result
Output: x: 7, y: 3 — the values are swapped successfully.
Knowing you can modify multiple variables in one function call expands how you can design functions for direct updates.
5
IntermediateIn-out parameters and function return values
🤔
Concept: In-out parameters let functions change variables without returning new values, simplifying code when multiple changes happen.
Instead of returning new values, you can update variables directly: func updateScores(score1: inout Int, score2: inout Int) { score1 += 10 score2 += 20 } var s1 = 50 var s2 = 70 updateScores(score1: &s1, score2: &s2) print(s1, s2)
Result
Output: 60 90 — both scores updated without returning anything.
This shows how in-out parameters can reduce the need for multiple return values and make code cleaner.
6
AdvancedMemory safety with in-out parameters
🤔Before reading on: Do you think passing the same variable twice as in-out parameters is allowed? Predict yes or no.
Concept: Swift enforces memory safety by preventing simultaneous access conflicts with in-out parameters.
You cannot pass the same variable to two in-out parameters at once: func addBoth(a: inout Int, b: inout Int) { a += b } var num = 5 // addBoth(a: &num, b: &num) // Error: overlapping accesses This prevents unsafe memory access and data corruption.
Result
Compiler error prevents unsafe simultaneous modification of the same variable.
Understanding Swift's safety rules helps avoid subtle bugs and crashes caused by conflicting variable access.
7
ExpertIn-out parameters and value types optimization
🤔Before reading on: Do you think in-out parameters always copy the entire value? Predict yes or no.
Concept: Swift uses copy-on-write optimization with in-out parameters for large value types to avoid unnecessary copying.
When passing large structs as in-out parameters, Swift delays copying until a write happens. This means: struct BigStruct { var data: [Int] } func modify(big: inout BigStruct) { big.data.append(1) } var bigValue = BigStruct(data: [0]) modify(big: &bigValue) Swift only copies the data if needed, improving performance.
Result
Efficient memory use with large value types passed as in-out parameters.
Knowing this optimization helps write performant code without fearing in-out parameters cause heavy copying.
Under the Hood
When a variable is passed as an in-out parameter, Swift temporarily creates an alias to the original variable's memory location. The function works directly on this memory, so any changes affect the original. At the call site, the variable is passed with '&' to indicate this aliasing. Swift also enforces exclusive access rules to prevent simultaneous conflicting modifications, ensuring memory safety. For large value types, Swift uses copy-on-write to avoid unnecessary copying until mutation occurs.
Why designed this way?
Swift was designed with safety and performance in mind. Passing copies by default avoids unintended side effects, but sometimes direct mutation is needed. In-out parameters provide a clear, explicit way to allow mutation, marked by '&' and 'inout' to avoid confusion. The exclusive access rules prevent hard-to-debug bugs from simultaneous writes. Copy-on-write optimizations balance performance with value semantics, keeping Swift safe and fast.
Caller stack frame:
┌───────────────┐
│ var x = 10    │
└─────┬─────────┘
      │ &x (address)
Function stack frame:
┌─────────────────────────┐
│ func modify(value: inout Int) │
│ value points to x's memory     │
│ value += 5                    │
└─────────────────────────┘
After return:
x in caller is now 15

Exclusive access check:
Cannot have two inout parameters aliasing same variable at once
Myth Busters - 4 Common Misconceptions
Quick: Does passing a variable as in-out parameter mean the function gets a copy? Commit to yes or no.
Common Belief:Passing a variable as in-out still passes a copy, so changes inside don't affect the original.
Tap to reveal reality
Reality:In-out parameters pass a reference (alias) to the original variable's memory, so changes inside do affect the original variable.
Why it matters:Believing this causes confusion when changes don't appear outside functions, leading to bugs or unnecessary code restructuring.
Quick: Can you pass a constant or a literal as an in-out parameter? Commit to yes or no.
Common Belief:You can pass any value, including constants or literals, as in-out parameters.
Tap to reveal reality
Reality:Only variables can be passed as in-out parameters because the function needs to modify actual storage.
Why it matters:Trying to pass constants or literals causes compiler errors, frustrating beginners who expect flexibility.
Quick: Is it safe to pass the same variable twice as two in-out parameters? Commit to yes or no.
Common Belief:Passing the same variable twice as in-out parameters is allowed and works fine.
Tap to reveal reality
Reality:Swift forbids this because it causes overlapping memory access, which is unsafe and can corrupt data.
Why it matters:Ignoring this leads to runtime crashes or unpredictable behavior, so understanding this rule is critical for safe code.
Quick: Do in-out parameters always cause a full copy of large structs? Commit to yes or no.
Common Belief:In-out parameters always copy the entire value, which can be slow for large structs.
Tap to reveal reality
Reality:Swift uses copy-on-write optimization, so copying happens only if the value is mutated, improving performance.
Why it matters:Knowing this prevents premature optimization worries and helps write efficient code without fear.
Expert Zone
1
In-out parameters require exclusive access to the variable for the function's duration, preventing simultaneous conflicting modifications.
2
Copy-on-write optimization means large value types passed as in-out parameters avoid copying until mutation, balancing safety and speed.
3
Using in-out parameters with reference types (classes) modifies the reference itself, not the object it points to, which can be subtle.
When NOT to use
Avoid in-out parameters when you want pure functions without side effects or when working with immutable data. Instead, use return values to produce new data. Also, for complex data sharing, consider reference types or other patterns like closures or delegates.
Production Patterns
In-out parameters are commonly used for swapping values, updating multiple variables at once, or performing in-place mutations efficiently. They appear in Swift standard library functions like 'swap(&a, &b)' and are used in performance-critical code to avoid unnecessary copying.
Connections
Pointers in C
In-out parameters in Swift are similar to passing pointers in C, where functions receive memory addresses to modify variables directly.
Understanding pointers helps grasp how in-out parameters allow direct memory access safely in Swift, but with added safety checks.
Pass-by-reference in other languages
In-out parameters implement pass-by-reference semantics, like in C++ or C#, allowing functions to modify caller variables.
Knowing pass-by-reference concepts from other languages clarifies why Swift uses explicit syntax for in-out parameters to avoid hidden side effects.
Bank account ledger updates
Updating a variable via in-out parameters is like a bank teller updating your account balance directly rather than giving you a new balance to write down.
This real-world connection shows how direct updates simplify processes and reduce errors compared to copying and replacing.
Common Pitfalls
#1Trying to pass a constant as an in-out parameter.
Wrong approach:let constantValue = 10 func increment(value: inout Int) { value += 1 } increment(value: &constantValue)
Correct approach:var variableValue = 10 func increment(value: inout Int) { value += 1 } increment(value: &variableValue)
Root cause:Constants cannot be modified, so passing them as in-out parameters, which require mutation, causes errors.
#2Passing the same variable twice to a function with two in-out parameters.
Wrong approach:var num = 5 func addBoth(a: inout Int, b: inout Int) { a += b } addBoth(a: &num, b: &num)
Correct approach:var num1 = 5 var num2 = 10 func addBoth(a: inout Int, b: inout Int) { a += b } addBoth(a: &num1, b: &num2)
Root cause:Swift forbids overlapping access to the same variable to ensure memory safety and prevent data corruption.
#3Forgetting to use '&' when passing a variable to an in-out parameter.
Wrong approach:var count = 3 func increment(value: inout Int) { value += 1 } increment(value: count) // Missing &
Correct approach:var count = 3 func increment(value: inout Int) { value += 1 } increment(value: &count)
Root cause:The '&' signals passing the variable's address; omitting it means passing a copy, causing a compiler error.
Key Takeaways
In-out parameters let functions modify variables directly by passing their memory address with '&' and marking parameters as 'inout'.
They provide a clear, explicit way to allow mutation, improving code clarity when multiple variables need updating.
Swift enforces strict rules for in-out parameters to ensure memory safety, such as forbidding passing constants or overlapping variables.
Copy-on-write optimization helps keep in-out parameters efficient for large value types by delaying copying until mutation.
Understanding in-out parameters connects to broader programming concepts like pointers and pass-by-reference, enriching your coding skills.