В Swift у меня есть собственная структура Deque с различными методами изменения для добавления и удаления элементов. Когда я связываю вызовы методов, например Deque.pushBack(contentsOf: ...), я сталкиваюсь с ошибкой «Невозможно использовать мутирующий элемент для неизменяемого значения: вызов функции возвращает неизменяемое значение». Однако когда я использую тот же шаблон цепочки для такой переменной, как b, он работает нормально. Может кто-нибудь объяснить, почему это происходит? Связано ли это с тем, что функция сопоставления является глобальной функцией? Как я могу решить эту проблему?
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 ведет себя по-разному в этих сценариях и как я могу обеспечить согласованное поведение при объединении вызовов методов в пользовательские структуры данных. Будем очень признательны за любые идеи или объяснения! (Я новичок в этом, если вы сможете объяснить в общих чертах, было бы здорово, спасибо)
Также объяснение в



Технической причины, по которой вам не разрешают это сделать, нет, но в большинстве случаев. вызов изменяющей функции по возвращаемому значению, скорее всего, является ошибкой. (См. также) Рассмотрим довольно надуманный пример:
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() и т. д.
крутое объяснение. То есть то же правило применяется, даже если я использую инфиксный оператор?
@Adrian Предполагая, что вы имеете в виду инфиксный оператор, такой как func +(inout deque: Deque, item: Int), да, вы не сможете вызвать его с возвращаемым значением функции в качестве LHS. Если вы не можете вызвать мутирующий метод для чего-либо, эту вещь также нельзя передать в качестве параметра inout.
func !><T, U>(_ f: ATescaping (T) -> U, _ g: ATescaping (U) -> Bool) -> (T) -> Bool Я использовал это, и да, он выдаст то же самое ошибка.
См. stackoverflow.com/a/48287958/1187415