Почему моя структура становится неизменной в цепочке методов? - PullRequest
0 голосов
/ 24 января 2019

В Swift я пытаюсь реализовать метод «tap», похожий на метод, который существует в Ruby.

Я придумал следующий пример кода:

private protocol Tap {
    mutating func tap(_ block: (inout Self) -> Void) -> Self
}

private extension Tap {
    mutating func tap(_ block: (inout Self) -> Void) -> Self {
        block(&self)
        return self
    }
}

extension Array: Tap {}

var a = Array(repeating: "Hello", count: 5)

a.tap {
    $0.append("5")
}.tap {
    $0.append("7")
}

print(a)  // (Expected) => ["Hello", "Hello", "Hello", "Hello", "Hello", "5", "7"]

Я не очень знаком с изменяющими функциями, параметрами inout или Swift в целом, но приведенный выше код выглядит так, как будто он должен работать для меня. tap работает должным образом, когда он не включен в цепочку методов. Когда я включаю его как часть цепочки методов, как в приведенном выше примере, компилятор Swift жалуется:

Невозможно использовать мутирующий член для неизменяемого значения: вызов функции возвращает неизменяемое значение

Может кто-нибудь объяснить мне, почему это не работает? Кто-нибудь может предоставить рабочее решение и объяснить, почему это решение работает?

Edit:

Другой пример использования будет:

let user = User(fromId: someId).tap {
   $0.firstName = someFirstName
   $0.lastName = someLastName
}

tap - это удобство, которое приходит от Ruby. В основном меня интересует понимание того, почему типы в моей функции работают неправильно.

1 Ответ

0 голосов
/ 24 января 2019

return self возвращает копию исходного массива, а не сам исходный массив.Пока эта копия не будет сохранена как var, она не может быть видоизменена.Итак, это будет работать:

var b = a.tap {
  $0.append("5")
}
b.tap {
  $0.append("7")
}

Но не без сохранения b как var в первую очередь.Конечно, вы бы не создавали b во-первых, вы просто использовали бы a несколько раз, как вы уже указали.

Итак, проблема в том, что вы можете выполнить tap один раз, но не может связать tap с.Это связано с тем, что возврат self неявно неизменен, и вы не можете вызывать мутирующую функцию для неизменяемого значения.Изменение tap на функцию без мутаций может дать вам то, что вы хотите:

private extension Tap {
    func tap(_ block: (inout Self) -> Void) -> Self {
        let copy = self
        block(&copy)
        return copy
    }
}

var a = Array(repeating: "Hello", count: 5)

a = a.tap({$0.append("5")}).tap({$0.append("7")})

Поскольку каждый вызов tap( возвращает копию оригинала, модифицированного данным блоком, вы можете вызвать его понеизменяемые типы.Это означает, что вы можете соединиться.

Единственный недостаток - это новый a = в начале.

...