Skip to content
Theory · Functions · lesson 5 of 9

Parameters and inout

A real food processor grinds up your apple, and it is no longer on the table. The function machine works differently: a copy goes into the opening, and the apple on the table stays whole. That is how Swift passes values into a function:

SWIFT
func withBonus(_ score: Int) -> Int {
    score + 10
}

var points = 50
let shown = withBonus(points)
print(points, shown) // 50 60

Let's see what happens:

  1. withBonus(points) passes a copy of the number 50 into the function.
  2. The function adds 10 to the copy and returns 60, and that number goes into shown.
  3. The variable points itself has not changed, and the program prints 50 60.

A parameter is a copy

A parameter gets a copy of the argument's value. Whatever the function does with its copy, the variable outside does not change. What's more, you may not change a parameter inside the body: it is a let constant:

SWIFT
func addBonus(_ score: Int) {
    score = score + 10 // ✗
}
TEXT
error: cannot assign to value: 'score' is a 'let' constant

This is the same message as for an assignment to an ordinary constant. This way Swift keeps you from changing a parameter by mistake: inside the body it always equals what the call passed in. Only the result comes out of the function.

The inout parameter

Sometimes a function does need to change a variable of whoever called it. In the first lesson of this theme, a function could not touch a var variable from the main part of the program. You can do it explicitly: pass the variable in the call, so the function can write its new value into it. For that, the parameter is marked with the word inout, and the argument gets an & in front:

SWIFT
func addBonus(_ score: inout Int) {
    score += 10
}

addBonus(&points)
print(points) // 60
addBonus(&points)
print(points) // 70

Let's see what is new here:

  • inout before the type lets the function change the parameter, and the change goes back into the variable outside;
  • the & before the argument is required: it shows anyone reading the call that points may change;
  • after each call points is 10 larger, so the program prints 60, then 70.

Such a parameter is called in-out: the value goes into the function and comes back out.

Only a var variable can be the argument for inout:

  • you cannot pass a let constant: after let base = 50, the call addBonus(&base) does not compile, with the error cannot pass immutable value as inout argument: 'base' is a 'let' constant;
  • you cannot pass a number written right in the code either: addBonus(&5) does not compile.

A copy in and back out

An in-out parameter works by the rule "copy on the way in, copy on the way out":

  1. In the call, the value of points is copied into the parameter score.
  2. The function changes its copy.
  3. When the function ends, the copy's value is written back into points.

Interviewers often ask this: is inout passed by reference, that is, does the function work on the variable itself rather than on a copy? By the rules of the language, no: the value is copied in and back out. The compiler may speed this up and work with the original variable directly, but the program must behave as if it were copied.

Common mistake

The & was forgotten in the call:

SWIFT
addBonus(points) // ✗
TEXT
error: passing value of type 'Int' to an inout parameter requires explicit '&'

The program does not compile. Swift requires an & on every argument for inout, so that the change to the variable is visible in the call itself. Without that sign, someone reading the code would think points is unchanged after the call. Add the &: addBonus(&points).

Key points

  • A parameter gets a copy of the argument and stays a constant inside the body: you can't change it, and the variable outside doesn't change.
  • inout before the type lets a function change a variable of whoever called it; in the call, the argument gets an & in front.
  • An in-out parameter works by copying: the value is copied into the function and written back when it ends.
  • Only a var variable can be the argument for inout, not a constant and not a number in the code.