Быстрая мутирующая функция как первоклассное значение

У меня может быть функция, чтобы поменять местами первые два элемента массива:

func swapFirstTwo(array: inout [Int]) {
  if array.count >= 2 {
    array.swapAt(0, 1)
  }
}

typealias Swapper = (inout [Int]) -> ()

// And I can have a variable = the function
let swapThem: Swapper = swapFirstTwo

// And it works like this:
var array = [1,2,3]
print(array)
swapThem(&array)
print(array)

// But I'm allergic to Global functions!
// It would be more swifty to have:

extension Array where Element == Int {
  mutating func swapFirstTwo2() {
    if count >= 2 {
      swapAt(0, 1)
    }
  }
}

typealias Swapper2 = (inout [Int]) -> () -> ()

// But when I do this:
let swapThemAgain: Swapper2 = Array.swapFirstTwo2
// I get the error:
// Partial application of 'mutating' method is not allowed; calling the function has undefined behavior and will be an error in future Swift versions

var array2 = [1,2,3]
print(array2)
array2.swapFirstTwo2()
print(array2)
// This in fact works but I've tried similar things and sometimes they appear to be unstable.
// How can I achieve doing: array2.swapFirstTwo2() without getting the error?

Это на самом деле работает, но я пробовал похожие вещи, и иногда они кажутся нестабильными. Также необходимо учитывать предупреждение компилятора. Как я могу добиться выполнения: array2.swapFirstTwo2() без получения предупреждения / ошибки?

1 ответ

Решение

Причина, по которой вы получаете предупреждение (и вскоре будет ошибка в режиме Swift 5):

extension Array where Element == Int { 
  mutating func swapFirstTwo2() {
    if count >= 2 {
      swapAt(0, 1)
    }
  }
}

typealias Swapper2 = (inout [Int]) -> () -> ()
let swapThemAgain: Swapper2 = Array.swapFirstTwo2

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

Так что, если вы должны были позвонить возвращенным (inout [Int]) -> () -> () с &array2, вы бы вернулись () -> () который теперь имеет недопустимую ссылку на array2, Попытка вызова этой функции приведет к неопределенному поведению, так как вы пытаетесь изменить inout аргумент за пределами окна, где он действителен.

Эта проблема будет исправлена, если / когда не примененные методы экземпляра получат плоские подписи, как Array.swapFirstTwo2 вместо этого будет оценивать (inout [Int]) -> (),

Но в то же время вы можете обойти проблему, используя вместо этого замыкание:

typealias Swapper2 = (inout [Int]) -> ()
let swapThemAgain: Swapper2 = { $0.swapFirstTwo2() }

var array2 = [1,2,3]
print(array2) // [1, 2, 3]
array2.swapFirstTwo2()
print(array2) // [2, 1, 3]
Другие вопросы по тегам