为什么 Swift 在某些链式调用中会抛出“无法在不可变值上使用变异成员”,而在其他链式调用中则不会?

问题描述 投票:0回答:1

在 Swift 中,我有一个自定义的 Deque 结构,其中包含用于添加和删除元素的各种变异方法。当我链接 Deque.pushBack(contentsOf: ...) 等方法调用时,遇到错误“无法在不可变值上使用变异成员:函数调用返回不可变值”。然而,当我在像 b 这样的变量上使用相同的链接模式时,它工作得很好。有人可以解释为什么会发生这种情况吗?这与 match 函数是全局函数有关吗?我该如何解决这个问题?

 struct Deque {
     private var arrayDeque: [Int] = []
     
     mutating func pushFront(_ element: Int) {
         arrayDeque.insert(element, at: 0)
     }
     
     @discardableResult
     mutating func pushFront(contentsOf elements: Int...) -> Deque {
         for element in elements.reversed() {
             pushFront(element)
         }
         return self
     }
     
     mutating func pushBack(_ element: Int) {
         arrayDeque.append(element)
     }
     
     @discardableResult
     mutating func pushBack(contentsOf elements: Int...) -> Deque {
         for element in elements {
             pushBack(element)
         }
         return self
     }
     
     @discardableResult
     mutating func popFront() -> Deque? {
         guard !isEmpty else { return nil }
         var copy = self
         copy.arrayDeque.removeFirst()
         return copy
     }
     
     @discardableResult
     mutating func popBack() -> Deque? {
         guard !isEmpty else { return nil }
         var copy = self
         copy.arrayDeque.removeLast()
         return copy
     }
     
     var isEmpty: Bool {
         return arrayDeque.isEmpty
     }
     
     var count: Int {
         return arrayDeque.count
     }
     
     func peekFront() -> Int? {
         return arrayDeque.first
     }
     
     func peekBack() -> Int? {
         return arrayDeque.last
     }
 }

 func match(_ xs: Deque) -> Bool { ... }

 // gives me error
 let a = match(Deque().pushBack(contentsOf: 1, 2, 1, 2, 1))

 // works fine
 var b = Deque()
 let b2 = b.pushBack(contentsOf: 1, 2, 1, 2, 1)
 let b3 = match(b2)

错误:无法在不可变值上使用变异成员:函数调用返回不可变值

我试图理解为什么 Swift 在这些场景中表现不同,以及如何在链接方法调用自定义数据结构时确保行为一致。任何见解或解释将不胜感激! (我是新手,如果你能用基本术语解释那就太好了,谢谢)

还有 chatGPT 解释:“实例的可变性:如果您在实例上链接可变方法,请确保实例本身是可变的(用 var 声明)。否则,您将无法应用实例所做的更改方法。”,以及关于如何“是因为在 Swift 中,当你像示例 a 那样链接方法调用时,中间结果是不可变的。”

arrays swift struct mutating-function
1个回答
0
投票

没有任何“技术”原因不允许您这样做,但在大多数情况下。对返回值调用变异函数很可能是一个错误。 (另请参阅)考虑一个相当人为的示例: var deque = Deque() func returnADeque() -> Deque { return deque } // suppose this is allowed returnADeque().pushBack(1)

这实际上不会修改全局变量
deque

Deque
是一个结构体(值类型),因此
returnADeque
返回它的副本。
这同样适用于初始化器。从调用者的角度来看,初始化器只是一个函数,它返回它正在初始化的任何类型的实例。假设 

pushBack

没有

return self
,那么
Deque().pushBack(contentsOf: 1, 2, 1, 2, 1)
不会做任何有用的工作。您创建一个
Deque
,然后将一些数字推入其中,然后丢弃它,而不使用您推入的任何内容。
要执行此链接,您可以将 

Deque

更改为具有不同语义的类(引用类型)。您还可以为每个函数制作两个版本 - 一种不是

mutating
并返回
self
用于链接,另一种是
mutating
并且不返回
self
 mutating func pushBack(contentsOf elements: Int...) {
     for element in elements {
         pushBack(element)
     }
 }

func pushedBack(contentsOf elements: Int...) -> Deque {
    var copy = self
    // normally you can reuse the mutating function here, but because pushBack
    // takes a variadic Int..., it is not possible in this case
    for element in elements {
        copy.pushBack(element)
    }
    return copy
}

允许链接的方法应使用动词的过去分词,以遵循标准库中使用的
命名约定

shuffle()

shuffled()
reverse()
reversed()
    

© www.soinside.com 2019 - 2024. All rights reserved.