DidSet в Swift имеет странный эффект постукивания на мутирующий func

Я только что узнал, что функция mutating func - это всего лишь функция curried с первым параметром как inout, поэтому приведенный ниже код будет работать и изменить firstName на "John"

struct Person {
    var firstName = "Matt"

    mutating func changeName(fn: String) {
        firstName = fn
    }
}
var p = Person() 
let changer = Person.changeName
changer(&p)("John")
p.firstName

но странная вещь, когда я добавляю наблюдателя свойств к p, как показано ниже, вы можете видеть, что firstName по-прежнему "Мэтт", почему? введите описание изображения здесь

Ответы

Ответ 1

в новом принятом предложении 0042-flatten-method-type, self больше не передается как функция с картой, поэтому эта проблема решена в Swift 3

Ответ 2

Интересная заметка о том, что наблюдатель называется до того, как теневой сеттер называется:

struct Person {
    var firstName = "Matt"

    mutating func changeName(fn: String) {
        firstName = fn
    }
}

var p: Person = Person() {
    didSet {
        print("p was set")
    }
}

print("1: \(p.firstName)")
let changer = Person.changeName
print("2: \(p.firstName)")
let setter = changer(&p)
print("3: \(p.firstName)")
setter("John")
print("4: \(p.firstName)")
p.changeName("John")
print("5: \(p.firstName)")

Отпечатки:

1: Matt
2: Matt
p was set
3: Matt
4: Matt
p was set
5: John

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

Когда мы изменим способ получения предварительно заполненного карточного ридера:

let setter = p.changeName

... объекты компилятора:

error: partial application of 'mutating' method is not allowed

Кажется, что компилятор понимает, что закрытие значения inout - плохая идея, так как в основном используется ссылка на тип значения.

Закрытие позволит вам изменить значение структуры в любое время, даже когда компилятор предполагает, что он будет постоянным. Чтобы предотвратить эту неудачную ситуацию, компилятор просто запрещает закрытие входа inout.

Вы нашли случай, который обманывает компилятор и работает вокруг диагностики. Это, кажется, ошибка, и она должна быть подана.

Краткая версия:

struct Foo {
    mutating func foo() {}
}

var f = Foo()
let m = Foo.foo
let s = m(&f)

Одна из двух последних строк должна испускать ошибку, похожую на let x = f.foo.