Перейти к содержимому
Теория · Функции · урок 2 из 9

Метки аргументов

В прошлом уроке таблички над отверстиями комбайна совпадали с именами параметров: add(a: 2, b: 3). Но табличка нужна тому, кто кладёт продукты, а имя внутри — самому комбайну. Swift позволяет сделать их разными, чтобы вызов читался как фраза:

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

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

Вызов sayHello(to: "Anna") читается почти как английская фраза: «скажи привет Анне». А внутри тела слово to было бы странным именем, поэтому там значение называется name.

Метка и имя параметра

Запись to name: String в заголовке состоит из трёх частей:

  • to — метка аргумента, её пишут при вызове;
  • name — имя параметра, под ним значение видно внутри тела;
  • String — тип параметра.

Если метку не указать отдельно, как в func add(a: Int, b: Int), имя параметра служит и меткой. Поэтому такую функцию вызывают как add(a: 2, b: 3).

Метка существует только снаружи функции. Если внутри тела sayHello написать \(to), компилятор ответит cannot find 'to' in scope.

Метки, отличные от имён параметров, помогают, когда параметров несколько. Функция func distance(from start: Int, to end: Int) -> Int вызывается как distance(from: 3, to: 10) — «расстояние от 3 до 10». Порядок аргументов при этом остаётся тем же, что в заголовке: вызов distance(to: 10, from: 3) не скомпилируется с ошибкой argument 'from' must precede argument 'to'.

Этот вопрос часто задают на собеседовании: чем метка аргумента отличается от имени параметра? Ответ:

  • метку пишут при вызове, и она делает вызов понятным;
  • имя параметра используют внутри тела функции;
  • если метку не задать, имя параметра служит и меткой.

Вызов без метки: _

Иногда метка только мешает. В вызове square(x: 4) буква x ничего не добавляет: и так ясно, что квадрат считают от четырёх. Чтобы убрать метку, на её место пишут _:

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

print(square(4)) // 16

Знак _ здесь значит «метки нет», и аргумент передают без неё. Теперь, наоборот, метку писать нельзя: square(x: 4) не скомпилируется с ошибкой extraneous argument label 'x:' in call.

Так объявлена и функция print: у её первого параметра метки нет. Поэтому значения для печати пишут без меток, а метки есть только у separator: и terminator:.

Значение по умолчанию

Параметру можно дать значение по умолчанию: после типа пишут = и значение. Тогда при вызове этот аргумент можно пропустить:

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

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

Разберём оба вызова:

  1. В welcome("Anna") аргумента with: нет, и greeting получает значение по умолчанию "Hello".
  2. welcome("Boris", with: "Hi") передаёт своё значение, и оно заменяет значение по умолчанию.

Вот почему у print можно не писать separator: и terminator:: у этих параметров есть значения по умолчанию — пробел и перевод строки.

Здесь картинка с комбайном перестаёт работать. У настоящего комбайна нет отверстия, которое само наполняется, если в него ничего не положили. А параметр со значением по умолчанию получает это значение каждый раз, когда аргумент пропущен.

Частая ошибка

Метку не написали, потому что аргумент всего один и путаницы нет:

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

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

Программа не компилируется. В Swift метка — часть вызова: если у параметра нет _, метку пишут обязательно, даже когда аргумент один. Метки проверяет компилятор, поэтому программа не запустится вовсе, и строки выше этого вызова тоже не выполнятся.

Обратите внимание: сообщение называет метку of:, а не имя параметра n. Исправить ошибку можно двумя способами:

  • дописать метку: half(of: 10);
  • если метка не нужна, объявить параметр через _: func half(_ n: Int) -> Int.

Главное

  • Метку пишут при вызове, а имя параметра используют внутри тела: в to name: String метка — to, а имя — name.
  • Если отдельной метки нет, имя параметра служит меткой, и при вызове его пишут обязательно.
  • _ на месте метки убирает её: square(4), и тогда метку писать нельзя.
  • Аргумент для параметра со значением по умолчанию можно пропустить, как separator: у print.