Быстрая мутирующая функция как первоклассное значение - PullRequest
0 голосов
/ 02 ноября 2018

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

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 Ответ

0 голосов
/ 03 ноября 2018

Причина, по которой вы получаете предупреждение (и вскоре будет ошибка в режиме 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]
...