Функции
Чему вы научитесь:
- объявлять свои функции с параметрами и результатом и вызывать их;
- задавать метки аргументов, убирать их через
_и давать параметрам значения по умолчанию; - возвращать несколько значений кортежем и менять переменную через
inout; - объяснять, как рекурсивная функция вызывает саму себя и где она останавливается.

Представьте кухонный комбайн. Сверху у него отверстия для продуктов, и над каждым табличка. Вы кладёте продукты, нажимаете кнопку, и из носика выходит готовый сок. Как комбайн устроен внутри, знать не обязательно, а работает он снова и снова, с любыми продуктами.
Функция в программе — такой же комбайн. Вы уже вызывали функции, встроенные в Swift: print печатает значения, а type(of:) называет тип. Теперь вы научитесь писать свои.
Зачем свои функции? Если одно и то же вычисление нужно в программе много раз, его записывают один раз, дают ему имя и вызывают по имени. А если в вычислении найдётся ошибка, исправить её придётся только в одном месте.
Вот функция, которая складывает два числа:
func add(a: Int, b: Int) -> Int {
return a + b
}
let sum = add(a: 2, b: 3)
print(sum) // 5
print(add(a: 10, b: -4)) // 6Давайте разберём программу по шагам:
- Строки от
funcдо закрывающей фигурной скобки объявляют функциюadd. Сами по себе они ничего не выполняют: это описание комбайна. add(a: 2, b: 3)вызывает функцию: число2попадает вa, а3— вb.- Функция вычисляет
a + b, иreturnотдаёт5туда, откуда её вызвали. Это число попадает в константуsum. print(sum)печатает5.- Второй вызов работает с другими числами и даёт
6.
Объявление функции
Строка func add(a: Int, b: Int) -> Int — заголовок функции. Его части:
func— ключевое слово, с которого начинается объявление;add— имя функции, по нему её вызывают;(a: Int, b: Int)— параметры в круглых скобках, через запятую: имя и тип каждого;-> Int— тип результата, который функция возвращает.
За заголовком в фигурных скобках стоит тело функции — код, который выполняется при каждом вызове.
Параметр — имя, под которым тело функции видит переданное значение. В комбайне это отверстие с табличкой: a и b — два отверстия для двух чисел. Внутри тела параметр ведёт себя как константа let: его читают, но не меняют.
Вызов и аргументы
При вызове после имени функции в скобках пишут аргументы — значения для параметров. Перед каждым аргументом стоит имя параметра с двоеточием: a: 2. Это метка аргумента, такая же, как separator: у print. Аргументы идут в том же порядке, что и параметры в заголовке.
Вызов функции — это выражение, и его значение — результат функции. Поэтому вызов можно положить в константу, напечатать или передать аргументом в другой вызов. Вызов выполняется, когда программа доходит до его строки, и в sum попадает готовое число 5, а не сам вызов.
На собеседовании начинающего разработчика иногда спрашивают, чем параметр отличается от аргумента. Ответ:
- параметр — имя в объявлении функции:
aвfunc add(a: Int, b: Int); - аргумент — значение, которое передают при вызове:
2вadd(a: 2, b: 3).
Результат функции
Запись -> Int обещает, что функция вернёт Int. Возвращает его инструкция return: она вычисляет значение справа от себя, отдаёт его и сразу завершает функцию. Строки тела ниже return уже не выполняются.
Тело из одного выражения
Если тело функции — одно выражение, return можно не писать: Swift вернёт значение этого выражения сам. Так устроена функция square в примере ниже.
Функция без результата
Функция не обязана что-то возвращать. Функция greet ниже печатает приветствие, и стрелки -> в её заголовке нет. Тип результата у неё всё равно есть — Void, то есть «ничего». Писать -> Void можно, но обычно не пишут.
func square(x: Int) -> Int {
x * x
}
func greet(name: String) {
print("Hello, \(name)!")
}
print(square(x: 4)) // 16
greet(name: "Anna") // Hello, Anna!
print(add(a: square(x: 3), b: 1)) // 10Разберём три вызова:
square(x: 4)возвращает16, хотяreturnв теле нет.greet(name: "Anna")печатает строку сама, а результата не возвращает. Поэтому её вызывают отдельной строкой, а не внутриprint.- В последней строке
square(x: 3)стоит аргументом другого вызова. Сначала выполняется он и даёт9, затемaddполучает9и1и возвращает10.
Здесь картинка с комбайном неточна. Сок из комбайна вытечет, даже если стакан не подставили. А результат функции, который никуда не положили и не напечатали, Swift считает подозрительным: строка add(a: 2, b: 3) сама по себе даёт предупреждение result of call to 'add(a:b:)' is unused.
Частая ошибка
Функция пытается изменить переменную, объявленную прямо в main.swift:
var total = 0
func addToTotal(n: Int) {
total += n // ✗
}error: main actor-isolated var 'total' can not be mutated from a nonisolated contextПрограмма не компилируется. Сообщение говорит: переменная total закреплена за главной частью программы — строками main.swift вне всех функций, — и функции менять её нельзя. Прочитать такую переменную из функции тоже не выйдет. А константу let с числом или строкой функция прочитать может: её никто не изменит.
Причина — защита от одновременных изменений. Swift 6 следит, чтобы одну переменную не меняли сразу две части программы, работающие параллельно. В ваших программах такая часть пока одна, и идёт она сверху вниз. Но функцию можно вызвать из любой части, поэтому компилятор заранее закрывает ей доступ к переменным главной части программы.
Комбайн не лезет к вам в холодильник: всё нужное ему кладут в отверстия. Так и здесь: передайте значение параметром, а результат присвойте сами.
var total = 0
total = add(a: total, b: 5)
total = add(a: total, b: 7)
print(total) // 12Функция add получает total аргументом и возвращает сумму, а присваиваете сумму переменной total вы сами, уже вне функции.
Главное
- Функцию объявляют словом
func: имя, параметры с типами в круглых скобках, тип результата после->и тело в фигурных скобках. - При вызове перед аргументом пишут метку:
add(a: 2, b: 3); параметр — имя в объявлении, аргумент — значение в вызове. returnвозвращает результат и завершает функцию; тело из одного выражения возвращает его безreturn, а функция без->возвращаетVoid.- Функция не трогает переменные
varглавной части программы: значения ей передают параметрами, а результат она возвращает.