4

我写了一个扩展Array,允许我弹出最后一个元素并立即将其添加到另一个数组中:

extension Array {

    mutating func popLast(to otherArray: inout [Element]) -> Element? {
        guard self.count > 0 else { return nil }
        return otherArray.appendAndReturn(self.popLast()!)
    }

    mutating func appendAndReturn(_ element: Element) -> Element {
        self.append(element)
        return element
    }

}

操场上的这个简单示例就像一个魅力:

var newNumbers = [1,2,3,4,5,6,7,8,9]
var usedNumbers: [Int] = []

newNumbers.popLast(to: &usedNumbers)
print(usedNumbers) // [9]

for _ in newNumbers {
    newNumbers.popLast(to: &usedNumbers)
}

print(usedNumbers) // [9, 8, 7, 6, 5, 4, 3, 2, 1]

但是使用结构内部的扩展(警告后的代码)会给我这个警告:

同时访问参数‘self’,但修改需要独占访问;考虑复制到局部变量

struct Test {

    var newNumbers = [1,2,3,4,5,6,7,8,9]
    var usedNumbers: [Int] = []

    mutating func getNewNumber() -> Int? {
        return newNumbers.popLast(to: &usedNumbers)
    }

}

这只是一个警告,我的应用程序在预期的行为下运行得很好,但我很好奇这里是否真的存在危险。查看SE-0176,我了解警告的目标,如果我要使用它从我附加到的同一个数组中弹出最后一个元素,因为写时复制可能会搞砸。所以我猜它与结构有关。但是在同一个结构内的两个不同数组上使用它,我认为没有危险。我是否遗漏了什么,有没有办法编写可以规避潜在问题的扩展?

4

1 回答 1

3

更新:

您的代码现在无需修改即可工作。

正如@Hamish 在下面的评论中指出的那样,这是一个错误,现在已在 Xcode 9 beta 3 附带的 Swift 版本中修复。我还验证了它在使用Linux x86_64构建Swift Dev的IBM Swift Sandbox上工作。4.0(2017 年 7 月 13 日)


正如您在评论中发现的那样,问题是您需要对结构进行独占访问才能对其进行变异,但是您将对结构的一部分的引用传递给 inout 参数。这显然应该起作用,因为您正在访问结构的不同部分,但是由于错误,编译器在这里过于严格。

警告建议复制到局部变量。由于您的返回很复杂,我使用了一个defer语句来返回 to 的副本,newNumbersnewNumbers避免将调用结果存储在临时变量中:

struct Test {

    var newNumbers = [1,2,3,4,5,6,7,8,9]
    var usedNumbers: [Int] = []

    mutating func getNewNumber() -> Int? {
        var newNumbersCopy = newNumbers
        defer { newNumbers = newNumbersCopy }
        return newNumbersCopy.popLast(to: &usedNumbers)
    }

}

您还可以选择通过复制以下内容来修复它usedNumbers

mutating func getNewNumber() -> Int? {
    var usedNumbersCopy = usedNumbers
    defer { usedNumbers = usedNumbersCopy }
    return newNumbers.popLast(to: &usedNumbersCopy)
}
于 2017-06-27T10:31:13.060 回答