Параметры и inout
Настоящий комбайн перемалывает ваше яблоко, и на столе его больше нет. Комбайн-функция устроен иначе: в отверстие попадает копия, а яблоко на столе остаётся целым. Так Swift передаёт значения в функцию:
func withBonus(_ score: Int) -> Int {
score + 10
}
var points = 50
let shown = withBonus(points)
print(points, shown) // 50 60Давайте разберём, что происходит:
withBonus(points)передаёт в функцию копию числа50.- Функция прибавляет к копии
10и возвращает60, а это число попадает вshown. - Сама переменная
pointsне изменилась, и программа печатает50 60.
Параметр — копия
Параметр получает копию значения аргумента. Что бы функция ни делала со своей копией, переменная снаружи не изменится. Менять параметр внутри тела нельзя — он константа let:
func addBonus(_ score: Int) {
score = score + 10 // ✗
}error: cannot assign to value: 'score' is a 'let' constantЭто то же сообщение, что и при присваивании обычной константе. Так Swift не даёт изменить параметр по ошибке: внутри тела он всегда равен тому, что передали при вызове. Наружу из функции выходит только результат.
Параметр inout
Иногда функция как раз должна изменить переменную того, кто её вызвал. В первом уроке темы функция не могла тронуть переменную var из главной части программы. Это можно сделать явно: передать переменную при вызове, чтобы функция записала в неё новое значение. Для этого параметр помечают словом inout, а перед аргументом ставят &:
func addBonus(_ score: inout Int) {
score += 10
}
addBonus(&points)
print(points) // 60
addBonus(&points)
print(points) // 70Разберём, что здесь нового:
inoutперед типом разрешает функции менять параметр, и изменение вернётся в переменную снаружи;&перед аргументом обязателен: по нему при чтении вызова видно, чтоpointsможет измениться;- после каждого вызова
pointsбольше на10, поэтому программа печатает60, а потом70.
Такой параметр называют сквозным: значение проходит через функцию и возвращается в ту же переменную. В разговоре и на собеседованиях его чаще называют inout-параметром.
Аргументом для inout может быть только переменная var:
- константу
letпередать нельзя: послеlet base = 50вызовaddBonus(&base)не скомпилируется с ошибкойcannot pass immutable value as inout argument: 'base' is a 'let' constant; - число, записанное прямо в коде, тоже нельзя:
addBonus(&5)не скомпилируется.
Копия туда и обратно
Сквозной параметр работает по правилу «копия на входе, копия на выходе»:
- При вызове значение
pointsкопируется в параметрscore. - Функция меняет свою копию.
- Когда функция завершается, значение копии записывается обратно в
points.
Этот вопрос часто задают на собеседовании: передаётся ли inout по ссылке, то есть работает ли функция с самой переменной, а не с копией? По правилам языка — нет: значение копируется туда и обратно. Компилятор может ускорить это и работать прямо с исходной переменной, но программа обязана вести себя так, как при копировании.
Частая ошибка
При вызове забыли &:
addBonus(points) // ✗error: passing value of type 'Int' to an inout parameter requires explicit '&'Программа не компилируется. Swift требует & у каждого аргумента для inout, чтобы изменение переменной было видно в самом вызове. Без этого значка читатель кода решил бы, что после вызова points осталась прежней. Допишите &: addBonus(&points).
Главное
- Параметр получает копию аргумента и внутри тела остаётся константой: менять его нельзя, и переменная снаружи не меняется.
inoutперед типом позволяет функции изменить переменную того, кто её вызвал; перед аргументом при вызове ставят&.- Сквозной параметр работает копированием: значение копируется в функцию и записывается обратно, когда она завершается.
- Аргументом для
inoutможет быть только переменнаяvar, но не константа и не число в коде.