Skip to content
Theory · Functions · lesson 2 of 9

Argument labels

In the previous lesson, the labels over the food processor's openings matched the parameter names: add(a: 2, b: 3). But the label is for whoever puts the ingredients in, and the name inside is for the machine itself. Swift lets you make them different, so that a call reads like a phrase:

SWIFT
func sayHello(to name: String) {
    print("Hello, \(name)!")
}

sayHello(to: "Anna") // Hello, Anna!

The call sayHello(to: "Anna") reads almost like an English phrase: "say hello to Anna". Inside the body, though, the word to would be a strange name, so there the value is called name.

The label and the parameter name

The part to name: String in the header has three pieces:

  • to is the argument label, written in the call;
  • name is the parameter name, under which the body sees the value;
  • String is the parameter's type.

If you don't give a separate label, as in func add(a: Int, b: Int), the parameter name serves as the label too. That is why such a function is called as add(a: 2, b: 3).

A label exists only outside the function. If you write \(to) inside the body of sayHello, the compiler answers cannot find 'to' in scope.

Labels that differ from the parameter names help when there are several parameters. The function func distance(from start: Int, to end: Int) -> Int is called as distance(from: 3, to: 10), "the distance from 3 to 10". The order of the arguments stays the same as in the header: the call distance(to: 10, from: 3) does not compile, with the error argument 'from' must precede argument 'to'.

Interviewers often ask this: how does an argument label differ from a parameter name? The answer:

  • the label is written in the call and makes the call clear;
  • the parameter name is used inside the function's body;
  • if you set no label, the parameter name serves as the label too.

A call without a label: _

Sometimes a label only gets in the way. In the call square(x: 4), the letter x adds nothing: it is clear anyway that the square is taken of four. To remove the label, you write _ in its place:

SWIFT
func square(_ x: Int) -> Int {
    x * x
}

print(square(4)) // 16

Here _ means "no label", and the argument is passed without one. Now, the other way round, you may not write a label: square(x: 4) does not compile, with the error extraneous argument label 'x:' in call.

The print function is declared this way too: its first parameter has no label. That is why the values to print go without labels, and only separator: and terminator: have labels.

Default values

A parameter can have a default value: after the type you write = and the value. Then the call may leave that argument out:

SWIFT
func welcome(_ name: String, with greeting: String = "Hello") {
    print("\(greeting), \(name)!")
}

welcome("Anna") // Hello, Anna!
welcome("Boris", with: "Hi") // Hi, Boris!

Let's look at both calls:

  1. welcome("Anna") has no with: argument, and greeting gets the default value "Hello".
  2. welcome("Boris", with: "Hi") passes its own value, and it replaces the default.

That is why print lets you leave out separator: and terminator:: these parameters have default values, a space and a line break.

Here the food processor picture stops working. A real machine has no opening that fills itself when you put nothing into it. But a parameter with a default value gets that value every time its argument is left out.

Common mistake

The label was left out because there is only one argument and nothing to mix up:

SWIFT
func half(of n: Int) -> Int {
    n / 2
}

print(half(10)) // ✗
TEXT
error: missing argument label 'of:' in call

The program does not compile. In Swift the label is part of the call: if a parameter has no _, its label is required, even when there is only one argument. Labels are checked by the compiler, so the program does not start at all, and the lines above this call don't run either.

Note that the message names the label of:, not the parameter name n. There are two ways to fix the error:

  • add the label: half(of: 10);
  • if the label is not needed, declare the parameter with _: func half(_ n: Int) -> Int.

Key points

  • The label is written in the call, and the parameter name is used inside the body: in to name: String the label is to and the name is name.
  • Without a separate label, the parameter name serves as the label, and the call must write it.
  • _ in place of the label removes it: square(4), and then the label may not be written.
  • An argument for a parameter with a default value can be left out, like separator: in print.