Что такое inout параметр в Swift и как он работает с value типами
Что проверяют
Знаете ли вы, что inout в Swift это не передача по ссылке, а модель copy-in copy-out, и понимаете ли, что из неё следует. Следом обычно спрашивают про закон эксклюзивного доступа и про то, зачем inout параметру типа класса.
Как отвечать
Параметры функции в Swift по умолчанию константы. inout разрешает функции изменить переменную вызывающего кода: аргумент передаётся с &, значение копируется внутрь, а при возврате записывается обратно.
func clamp(_ value: inout Int, to range: ClosedRange<Int>) {
value = min(max(value, range.lowerBound), range.upperBound)
}
var volume = 140
clamp(&volume, to: 0...100) // volume == 100Для хранимой переменной компилятор обычно превращает это в работу по адресу, но семантика остаётся copy-in copy-out, и это заметно:
- наблюдатели:
willSetиdidSetпереданного свойства сработают один раз, после выхода из функции; - вычисляемые свойства: передавать можно, getter вызовется на входе, setter на выходе;
- ограничения: нельзя передать
let, литерал или одну переменную дважды в одном вызове, нельзя захватить параметр escaping-замыканием и задать ему значение по умолчанию; - эксклюзивность: со Swift 5 перекрывающиеся доступы к одной переменной ловятся при компиляции, а для глобальных переменных и свойств классов проверяются в рантайме с падением «Simultaneous accesses».
С value-типами это хорошо сочетается с copy-on-write: массив через inout не копируется, и append меняет тот же буфер, если других ссылок на него нет. На том же механизме держится mutating: в таком методе self неявно передаётся как inout.
С классами inout тоже работает, только смысл другой: функция может заменить саму ссылку на другой объект, а не только поменять его свойства.
Где спотыкаются
Называют inout передачей по ссылке и ждут, что изменения видны снаружи ещё до возврата из функции.
Утверждают, что inout бывает только у value-типов. Для класса он нужен, когда надо переприсвоить ссылку вызывающего кода.
Пытаются передать свойство актора как inout в async функцию. Компилятор запрещает это, потому что через точку await эксклюзивный доступ не гарантировать.